diff --git a/.idea/misc.xml b/.idea/misc.xml index 02de60b..8898ade 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,4 +3,5 @@ + \ No newline at end of file diff --git a/code/data/daily_basic.ipynb b/code/data/daily_basic.ipynb index 4e9a383..a9db707 100644 --- a/code/data/daily_basic.ipynb +++ b/code/data/daily_basic.ipynb @@ -5,8 +5,8 @@ "id": "18d1d622-b083-4cc4-a6f8-7c1ed2d0edd2", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:55:46.122736Z", - "start_time": "2025-02-11T15:55:46.106368Z" + "end_time": "2025-03-02T09:47:08.470810Z", + "start_time": "2025-03-02T09:47:07.512525Z" } }, "source": [ @@ -16,19 +16,35 @@ "pro = ts.pro_api()" ], "outputs": [], - "execution_count": 3 + "execution_count": 2 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:55:46.689986Z", - "start_time": "2025-02-11T15:55:46.130840Z" + "end_time": "2025-03-02T09:47:10.242731Z", + "start_time": "2025-03-02T09:47:08.470810Z" } }, "cell_type": "code", "source": [ "from datetime import datetime\n", "import pandas as pd\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "def filter_rows(df):\n", + " # 按照 name 和 start_date 分组\n", + " def select_row(group):\n", + " # 如果有 end_date 不为 NaT 的行,优先保留这些行\n", + " valid_rows = group[group['end_date'].notna()]\n", + " if not valid_rows.empty:\n", + " return valid_rows.iloc[0] # 返回第一个有效行\n", + " else:\n", + " return group.iloc[0] # 如果没有有效行,返回第一行\n", + "\n", + " filtered_df = df.groupby(['name', 'start_date'], group_keys=False).apply(select_row)\n", + " filtered_df = filtered_df.reset_index(drop=True)\n", + " return filtered_df\n", "\n", "def is_st(name_change_dict, stock_code, target_date):\n", " target_date = datetime.strptime(target_date, '%Y%m%d')\n", @@ -56,11 +72,11 @@ " # 只保留 'ST' 和 '*ST' 的记录\n", " st_data = group[(group['change_reason'] == 'ST') | (group['change_reason'] == '*ST')]\n", " if not st_data.empty:\n", - " name_change_dict[ts_code] = st_data" + " name_change_dict[ts_code] = filter_rows(st_data)" ], "id": "bc8f03e027027004", "outputs": [], - "execution_count": 4 + "execution_count": 3 }, { "cell_type": "code", @@ -68,8 +84,8 @@ "metadata": { "scrolled": true, "ExecuteTime": { - "end_time": "2025-02-11T16:15:26.024182Z", - "start_time": "2025-02-11T15:55:46.721189Z" + "end_time": "2025-03-02T08:33:15.997350Z", + "start_time": "2025-03-02T08:17:08.727232Z" } }, "source": [ @@ -135,6 +151,28 @@ "name": "stdout", "output_type": "stream", "text": [ + "任务 20250331 完成\n", + "任务 20250401 完成\n", + "任务 20250328 完成\n", + "任务 20250327 完成\n", + "任务 20250326 完成\n", + "任务 20250325 完成\n", + "任务 20250324 完成\n", + "任务 20250321 完成\n", + "任务 20250320 完成\n", + "任务 20250319 完成\n", + "任务 20250318 完成\n", + "任务 20250317 完成\n", + "任务 20250314 完成\n", + "任务 20250313 完成\n", + "任务 20250311 完成\n", + "任务 20250312 完成\n", + "任务 20250310 完成\n", + "任务 20250307 完成\n", + "任务 20250306 完成\n", + "任务 20250305 完成\n", + "任务 20250303 完成\n", + "任务 20250304 完成\n", "任务 20250227 完成\n", "任务 20250228 完成\n", "任务 20250226 完成\n", @@ -174,8 +212,8 @@ "任务 20241231 完成\n", "任务 20241230 完成\n", "任务 20241227 完成\n", - "任务 20241226 完成\n", "任务 20241225 完成\n", + "任务 20241226 完成\n", "任务 20241224 完成\n", "任务 20241223 完成\n", "任务 20241220 完成\n", @@ -188,8 +226,8 @@ "任务 20241211 完成\n", "任务 20241210 完成\n", "任务 20241209 完成\n", - "任务 20241206 完成\n", "任务 20241205 完成\n", + "任务 20241206 完成\n", "任务 20241204 完成\n", "任务 20241203 完成\n", "任务 20241202 完成\n", @@ -230,8 +268,8 @@ "任务 20241014 完成\n", "任务 20241011 完成\n", "任务 20241010 完成\n", - "任务 20241008 完成\n", "任务 20241009 完成\n", + "任务 20241008 完成\n", "任务 20240930 完成\n", "任务 20240927 完成\n", "任务 20240926 完成\n", @@ -239,8 +277,8 @@ "任务 20240924 完成\n", "任务 20240923 完成\n", "任务 20240920 完成\n", - "任务 20240918 完成\n", "任务 20240919 完成\n", + "任务 20240918 完成\n", "任务 20240913 完成\n", "任务 20240912 完成\n", "任务 20240911 完成\n", @@ -255,8 +293,8 @@ "任务 20240829 完成\n", "任务 20240828 完成\n", "任务 20240827 完成\n", - "任务 20240823 完成\n", "任务 20240826 完成\n", + "任务 20240823 完成\n", "任务 20240822 完成\n", "任务 20240821 完成\n", "任务 20240820 完成\n", @@ -274,13 +312,13 @@ "任务 20240802 完成\n", "任务 20240801 完成\n", "任务 20240731 完成\n", + "任务 20240729 完成\n", "任务 20240730 完成\n", "任务 20240726 完成\n", - "任务 20240729 完成\n", - "任务 20240724 完成\n", "任务 20240725 完成\n", - "任务 20240722 完成\n", + "任务 20240724 完成\n", "任务 20240723 完成\n", + "任务 20240722 完成\n", "任务 20240719 完成\n", "任务 20240718 完成\n", "任务 20240717 完成\n", @@ -292,26 +330,26 @@ "任务 20240709 完成\n", "任务 20240708 完成\n", "任务 20240705 完成\n", - "任务 20240703 完成\n", "任务 20240704 完成\n", + "任务 20240703 完成\n", "任务 20240702 完成\n", "任务 20240701 完成\n", + "任务 20240628 完成\n", "任务 20240627 完成\n", "任务 20240626 完成\n", "任务 20240625 完成\n", "任务 20240624 完成\n", - "任务 20240628 完成\n", "任务 20240621 完成\n", "任务 20240620 完成\n", "任务 20240619 完成\n", "任务 20240618 完成\n", "任务 20240617 完成\n", - "任务 20240613 完成\n", "任务 20240614 完成\n", + "任务 20240613 完成\n", "任务 20240612 完成\n", "任务 20240611 完成\n", - "任务 20240606 完成\n", "任务 20240607 完成\n", + "任务 20240606 完成\n", "任务 20240605 完成\n", "任务 20240604 完成\n", "任务 20240603 完成\n", @@ -322,12 +360,12 @@ "任务 20240527 完成\n", "任务 20240524 完成\n", "任务 20240523 完成\n", - "任务 20240521 完成\n", "任务 20240522 完成\n", + "任务 20240521 完成\n", "任务 20240520 完成\n", "任务 20240517 完成\n", - "任务 20240516 完成\n", "任务 20240515 完成\n", + "任务 20240516 完成\n", "任务 20240514 完成\n", "任务 20240513 完成\n", "任务 20240510 完成\n", @@ -339,23 +377,23 @@ "任务 20240429 完成\n", "任务 20240426 完成\n", "任务 20240425 完成\n", - "任务 20240424 完成\n", "任务 20240423 完成\n", + "任务 20240424 完成\n", "任务 20240422 完成\n", "任务 20240419 完成\n", - "任务 20240418 完成\n", "任务 20240417 完成\n", + "任务 20240418 完成\n", "任务 20240416 完成\n", "任务 20240415 完成\n", - "任务 20240411 完成\n", "任务 20240412 完成\n", + "任务 20240411 完成\n", "任务 20240410 完成\n", "任务 20240409 完成\n", "任务 20240408 完成\n", "任务 20240403 完成\n", "任务 20240402 完成\n", - "任务 20240329 完成\n", "任务 20240401 完成\n", + "任务 20240329 完成\n", "任务 20240328 完成\n", "任务 20240327 完成\n", "任务 20240326 完成\n", @@ -384,8 +422,8 @@ "任务 20240222 完成\n", "任务 20240221 完成\n", "任务 20240220 完成\n", - "任务 20240208 完成\n", "任务 20240219 完成\n", + "任务 20240208 完成\n", "任务 20240207 完成\n", "任务 20240206 完成\n", "任务 20240205 完成\n", @@ -405,12 +443,12 @@ "任务 20240116 完成\n", "任务 20240115 完成\n", "任务 20240112 完成\n", - "任务 20240110 完成\n", "任务 20240111 完成\n", + "任务 20240110 完成\n", "任务 20240109 完成\n", "任务 20240108 完成\n", - "任务 20240104 完成\n", "任务 20240105 完成\n", + "任务 20240104 完成\n", "任务 20240103 完成\n", "任务 20240102 完成\n", "任务 20231229 完成\n", @@ -424,16 +462,16 @@ "任务 20231219 完成\n", "任务 20231218 完成\n", "任务 20231215 完成\n", - "任务 20231213 完成\n", "任务 20231214 完成\n", + "任务 20231213 完成\n", "任务 20231212 完成\n", "任务 20231211 完成\n", - "任务 20231207 完成\n", "任务 20231208 完成\n", - "任务 20231205 完成\n", + "任务 20231207 完成\n", "任务 20231206 完成\n", - "任务 20231201 完成\n", + "任务 20231205 完成\n", "任务 20231204 完成\n", + "任务 20231201 完成\n", "任务 20231130 完成\n", "任务 20231129 完成\n", "任务 20231128 完成\n", @@ -455,18 +493,18 @@ "任务 20231106 完成\n", "任务 20231103 完成\n", "任务 20231102 完成\n", - "任务 20231101 完成\n", "任务 20231031 完成\n", + "任务 20231101 完成\n", "任务 20231030 完成\n", "任务 20231027 完成\n", "任务 20231026 完成\n", "任务 20231025 完成\n", - "任务 20231023 完成\n", "任务 20231024 完成\n", - "任务 20231020 完成\n", + "任务 20231023 完成\n", "任务 20231019 完成\n", - "任务 20231018 完成\n", + "任务 20231020 完成\n", "任务 20231017 完成\n", + "任务 20231018 完成\n", "任务 20231016 完成\n", "任务 20231013 完成\n", "任务 20231012 完成\n", @@ -480,39 +518,39 @@ "任务 20230922 完成\n", "任务 20230921 完成\n", "任务 20230920 完成\n", - "任务 20230918 完成\n", "任务 20230919 完成\n", - "任务 20230914 完成\n", + "任务 20230918 完成\n", "任务 20230915 完成\n", - "任务 20230912 完成\n", + "任务 20230914 完成\n", "任务 20230913 完成\n", - "任务 20230908 完成\n", + "任务 20230912 完成\n", "任务 20230911 完成\n", - "任务 20230906 完成\n", + "任务 20230908 完成\n", "任务 20230907 完成\n", + "任务 20230906 完成\n", "任务 20230905 完成\n", "任务 20230904 完成\n", "任务 20230901 完成\n", "任务 20230831 完成\n", "任务 20230830 完成\n", - "任务 20230828 完成\n", "任务 20230829 完成\n", + "任务 20230828 完成\n", "任务 20230825 完成\n", "任务 20230824 完成\n", "任务 20230823 完成\n", "任务 20230822 完成\n", "任务 20230821 完成\n", - "任务 20230817 完成\n", "任务 20230818 完成\n", + "任务 20230817 完成\n", "任务 20230816 完成\n", "任务 20230815 完成\n", "任务 20230814 完成\n", - "任务 20230811 完成\n", "任务 20230810 完成\n", + "任务 20230811 完成\n", "任务 20230809 完成\n", "任务 20230808 完成\n", - "任务 20230804 完成\n", "任务 20230807 完成\n", + "任务 20230804 完成\n", "任务 20230803 完成\n", "任务 20230802 完成\n", "任务 20230801 完成\n", @@ -520,10 +558,10 @@ "任务 20230728 完成\n", "任务 20230727 完成\n", "任务 20230726 完成\n", - "任务 20230724 完成\n", "任务 20230725 完成\n", - "任务 20230720 完成\n", + "任务 20230724 完成\n", "任务 20230721 完成\n", + "任务 20230720 完成\n", "任务 20230719 完成\n", "任务 20230718 完成\n", "任务 20230717 完成\n", @@ -533,8 +571,8 @@ "任务 20230711 完成\n", "任务 20230710 完成\n", "任务 20230707 完成\n", - "任务 20230705 完成\n", "任务 20230706 完成\n", + "任务 20230705 完成\n", "任务 20230704 完成\n", "任务 20230703 完成\n", "任务 20230630 完成\n", @@ -554,8 +592,8 @@ "任务 20230608 完成\n", "任务 20230607 完成\n", "任务 20230606 完成\n", - "任务 20230605 完成\n", "任务 20230602 完成\n", + "任务 20230605 完成\n", "任务 20230601 完成\n", "任务 20230531 完成\n", "任务 20230530 完成\n", @@ -569,8 +607,8 @@ "任务 20230518 完成\n", "任务 20230517 完成\n", "任务 20230516 完成\n", - "任务 20230512 完成\n", "任务 20230515 完成\n", + "任务 20230512 完成\n", "任务 20230511 完成\n", "任务 20230510 完成\n", "任务 20230509 完成\n", @@ -579,8 +617,8 @@ "任务 20230504 完成\n", "任务 20230428 完成\n", "任务 20230427 完成\n", - "任务 20230426 完成\n", "任务 20230425 完成\n", + "任务 20230426 完成\n", "任务 20230424 完成\n", "任务 20230421 完成\n", "任务 20230420 完成\n", @@ -593,14 +631,14 @@ "任务 20230411 完成\n", "任务 20230410 完成\n", "任务 20230407 完成\n", - "任务 20230406 完成\n", "任务 20230404 完成\n", + "任务 20230406 完成\n", "任务 20230403 完成\n", "任务 20230331 完成\n", "任务 20230330 完成\n", "任务 20230329 完成\n", - "任务 20230327 完成\n", "任务 20230328 完成\n", + "任务 20230327 完成\n", "任务 20230324 完成\n", "任务 20230323 完成\n", "任务 20230322 完成\n", @@ -618,8 +656,8 @@ "任务 20230306 完成\n", "任务 20230303 完成\n", "任务 20230302 完成\n", - "任务 20230228 完成\n", "任务 20230301 完成\n", + "任务 20230228 完成\n", "任务 20230227 完成\n", "任务 20230224 完成\n", "任务 20230223 完成\n", @@ -630,13 +668,13 @@ "任务 20230216 完成\n", "任务 20230215 完成\n", "任务 20230214 完成\n", - "任务 20230210 完成\n", "任务 20230213 完成\n", + "任务 20230210 完成\n", "任务 20230209 完成\n", "任务 20230208 完成\n", - "任务 20230206 完成\n", "任务 20230207 完成\n", "任务 20230203 完成\n", + "任务 20230206 完成\n", "任务 20230202 完成\n", "任务 20230201 完成\n", "任务 20230131 完成\n", @@ -656,8 +694,8 @@ "任务 20230104 完成\n", "任务 20230103 完成\n", "任务 20221230 完成\n", - "任务 20221228 完成\n", "任务 20221229 完成\n", + "任务 20221228 完成\n", "任务 20221227 完成\n", "任务 20221226 完成\n", "任务 20221223 完成\n", @@ -675,8 +713,8 @@ "任务 20221207 完成\n", "任务 20221206 完成\n", "任务 20221205 完成\n", - "任务 20221201 完成\n", "任务 20221202 完成\n", + "任务 20221201 完成\n", "任务 20221130 完成\n", "任务 20221129 完成\n", "任务 20221128 完成\n", @@ -951,8 +989,8 @@ "任务 20211021 完成\n", "任务 20211020 完成\n", "任务 20211019 完成\n", - "任务 20211015 完成\n", "任务 20211018 完成\n", + "任务 20211015 完成\n", "任务 20211014 完成\n", "任务 20211013 完成\n", "任务 20211012 完成\n", @@ -989,8 +1027,8 @@ "任务 20210819 完成\n", "任务 20210818 完成\n", "任务 20210817 完成\n", - "任务 20210813 完成\n", "任务 20210816 完成\n", + "任务 20210813 完成\n", "任务 20210812 完成\n", "任务 20210811 完成\n", "任务 20210810 完成\n", @@ -1045,8 +1083,8 @@ "任务 20210601 完成\n", "任务 20210531 完成\n", "任务 20210528 完成\n", - "任务 20210527 完成\n", "任务 20210526 完成\n", + "任务 20210527 完成\n", "任务 20210525 完成\n", "任务 20210524 完成\n", "任务 20210521 完成\n", @@ -1089,8 +1127,8 @@ "任务 20210325 完成\n", "任务 20210324 完成\n", "任务 20210323 完成\n", - "任务 20210319 完成\n", "任务 20210322 完成\n", + "任务 20210319 完成\n", "任务 20210318 完成\n", "任务 20210317 完成\n", "任务 20210316 完成\n", @@ -1155,8 +1193,8 @@ "任务 20201215 完成\n", "任务 20201214 完成\n", "任务 20201211 完成\n", - "任务 20201209 完成\n", "任务 20201210 完成\n", + "任务 20201209 完成\n", "任务 20201208 完成\n", "任务 20201207 完成\n", "任务 20201204 完成\n", @@ -1276,8 +1314,8 @@ "任务 20200617 完成\n", "任务 20200616 完成\n", "任务 20200615 完成\n", - "任务 20200611 完成\n", "任务 20200612 完成\n", + "任务 20200611 完成\n", "任务 20200610 完成\n", "任务 20200609 完成\n", "任务 20200608 完成\n", @@ -1288,16 +1326,16 @@ "任务 20200601 完成\n", "任务 20200529 完成\n", "任务 20200528 完成\n", - "任务 20200526 完成\n", "任务 20200527 完成\n", + "任务 20200526 完成\n", "任务 20200525 完成\n", "任务 20200522 完成\n", "任务 20200521 完成\n", "任务 20200520 完成\n", "任务 20200519 完成\n", "任务 20200518 完成\n", - "任务 20200514 完成\n", "任务 20200515 完成\n", + "任务 20200514 完成\n", "任务 20200513 完成\n", "任务 20200512 完成\n", "任务 20200511 完成\n", @@ -1306,8 +1344,8 @@ "任务 20200506 完成\n", "任务 20200430 完成\n", "任务 20200429 完成\n", - "任务 20200427 完成\n", "任务 20200428 完成\n", + "任务 20200427 完成\n", "任务 20200424 完成\n", "任务 20200423 完成\n", "任务 20200422 完成\n", @@ -1318,8 +1356,8 @@ "任务 20200415 完成\n", "任务 20200414 完成\n", "任务 20200413 完成\n", - "任务 20200409 完成\n", "任务 20200410 完成\n", + "任务 20200409 完成\n", "任务 20200408 完成\n", "任务 20200407 完成\n", "任务 20200403 完成\n", @@ -1426,8 +1464,8 @@ "任务 20191105 完成\n", "任务 20191104 完成\n", "任务 20191101 完成\n", - "任务 20191030 完成\n", "任务 20191031 完成\n", + "任务 20191030 完成\n", "任务 20191029 完成\n", "任务 20191028 完成\n", "任务 20191025 完成\n", @@ -1504,10 +1542,10 @@ "任务 20190710 完成\n", "任务 20190709 完成\n", "任务 20190708 完成\n", - "任务 20190704 完成\n", "任务 20190705 完成\n", - "任务 20190702 完成\n", + "任务 20190704 完成\n", "任务 20190703 完成\n", + "任务 20190702 完成\n", "任务 20190701 完成\n", "任务 20190628 完成\n", "任务 20190627 完成\n", @@ -1595,10 +1633,10 @@ "任务 20190226 完成\n", "任务 20190225 完成\n", "任务 20190222 完成\n", + "任务 20190221 完成\n", "任务 20190220 完成\n", "任务 20190219 完成\n", "任务 20190218 完成\n", - "任务 20190221 完成\n", "任务 20190215 完成\n", "任务 20190214 完成\n", "任务 20190213 完成\n", @@ -1624,8 +1662,8 @@ "任务 20190109 完成\n", "任务 20190108 完成\n", "任务 20190107 完成\n", - "任务 20190103 完成\n", "任务 20190104 完成\n", + "任务 20190103 完成\n", "任务 20190102 完成\n", "任务 20181228 完成\n", "任务 20181227 完成\n", @@ -1664,8 +1702,8 @@ "任务 20181112 完成\n", "任务 20181109 完成\n", "任务 20181108 完成\n", - "任务 20181107 完成\n", "任务 20181106 完成\n", + "任务 20181107 完成\n", "任务 20181105 完成\n", "任务 20181102 完成\n", "任务 20181101 完成\n", @@ -1822,8 +1860,8 @@ "任务 20180320 完成\n", "任务 20180319 完成\n", "任务 20180316 完成\n", - "任务 20180314 完成\n", "任务 20180315 完成\n", + "任务 20180314 完成\n", "任务 20180313 完成\n", "任务 20180312 完成\n", "任务 20180309 完成\n", @@ -1842,10 +1880,10 @@ "任务 20180213 完成\n", "任务 20180212 完成\n", "任务 20180209 完成\n", - "任务 20180208 完成\n", "任务 20180207 完成\n", - "任务 20180206 完成\n", + "任务 20180208 完成\n", "任务 20180205 完成\n", + "任务 20180206 完成\n", "任务 20180202 完成\n", "任务 20180201 完成\n", "任务 20180131 完成\n", @@ -1890,8 +1928,8 @@ "任务 20171206 完成\n", "任务 20171205 完成\n", "任务 20171204 完成\n", - "任务 20171130 完成\n", "任务 20171201 完成\n", + "任务 20171130 完成\n", "任务 20171129 完成\n", "任务 20171128 完成\n", "任务 20171127 完成\n", @@ -1904,8 +1942,8 @@ "任务 20171116 完成\n", "任务 20171115 完成\n", "任务 20171114 完成\n", - "任务 20171113 完成\n", "任务 20171110 完成\n", + "任务 20171113 完成\n", "任务 20171109 完成\n", "任务 20171108 完成\n", "任务 20171107 完成\n", @@ -1934,8 +1972,8 @@ "任务 20170928 完成\n", "任务 20170927 完成\n", "任务 20170926 完成\n", - "任务 20170925 完成\n", "任务 20170922 完成\n", + "任务 20170925 完成\n", "任务 20170921 完成\n", "任务 20170920 完成\n", "任务 20170919 完成\n", @@ -1948,18 +1986,18 @@ "任务 20170908 完成\n", "任务 20170907 完成\n", "任务 20170906 完成\n", - "任务 20170904 完成\n", "任务 20170905 完成\n", + "任务 20170904 完成\n", "任务 20170901 完成\n", "任务 20170831 完成\n", "任务 20170830 完成\n", "任务 20170829 完成\n", "任务 20170828 完成\n", "任务 20170825 完成\n", - "任务 20170824 完成\n", "任务 20170823 完成\n", - "任务 20170822 完成\n", + "任务 20170824 完成\n", "任务 20170821 完成\n", + "任务 20170822 完成\n", "任务 20170818 完成\n", "任务 20170817 完成\n", "任务 20170816 完成\n", @@ -1998,8 +2036,8 @@ "任务 20170630 完成\n", "任务 20170629 完成\n", "任务 20170628 完成\n", - "任务 20170627 完成\n", "任务 20170626 完成\n", + "任务 20170627 完成\n", "任务 20170623 完成\n", "任务 20170622 完成\n", "任务 20170621 完成\n", @@ -2028,10 +2066,10 @@ "任务 20170517 完成\n", "任务 20170516 完成\n", "任务 20170515 完成\n", - "任务 20170511 完成\n", "任务 20170512 完成\n", - "任务 20170509 完成\n", + "任务 20170511 完成\n", "任务 20170510 完成\n", + "任务 20170509 完成\n", "任务 20170508 完成\n", "任务 20170505 完成\n", "任务 20170504 完成\n", @@ -2050,8 +2088,8 @@ "任务 20170414 完成\n", "任务 20170413 完成\n", "任务 20170412 完成\n", - "任务 20170410 完成\n", "任务 20170411 完成\n", + "任务 20170410 完成\n", "任务 20170407 完成\n", "任务 20170406 完成\n", "任务 20170405 完成\n", @@ -2070,16 +2108,16 @@ "任务 20170315 完成\n", "任务 20170314 完成\n", "任务 20170313 完成\n", - "任务 20170310 完成\n", "任务 20170309 完成\n", + "任务 20170310 完成\n", "任务 20170308 完成\n", "任务 20170307 完成\n", "任务 20170306 完成\n", "任务 20170303 完成\n", "任务 20170302 完成\n", "任务 20170301 完成\n", - "任务 20170228 完成\n", "任务 20170227 完成\n", + "任务 20170228 完成\n", "任务 20170224 完成\n", "任务 20170223 完成\n", "任务 20170222 完成\n", @@ -2117,15 +2155,15 @@ ] } ], - "execution_count": 5 + "execution_count": 3 }, { "cell_type": "code", "id": "97fdf8be-a86c-4404-bf0c-701f002cd81c", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T16:15:26.646658Z", - "start_time": "2025-02-11T16:15:26.120701Z" + "end_time": "2025-03-02T08:33:16.498221Z", + "start_time": "2025-03-02T08:33:16.033912Z" } }, "source": [ @@ -2138,70 +2176,70 @@ "output_type": "stream", "text": [ " ts_code trade_date close turnover_rate turnover_rate_f \\\n", - "0 002512.SZ 20250211 5.03 5.9759 7.8713 \n", - "1 600966.SH 20250211 4.83 0.6904 1.3494 \n", - "2 600358.SH 20250211 3.68 8.5826 11.3780 \n", - "3 002893.SZ 20250211 9.73 1.9217 2.6415 \n", - "4 300648.SZ 20250211 22.90 1.7775 2.3188 \n", + "0 002977.SZ 20250227 27.20 2.2311 3.0411 \n", + "1 688065.SH 20250227 48.17 0.7081 1.4224 \n", + "2 002563.SZ 20250227 6.30 0.7054 2.2058 \n", + "3 300044.SZ 20250227 7.29 11.8529 13.2447 \n", + "4 603219.SH 20250227 17.96 5.9145 28.1559 \n", "... ... ... ... ... ... \n", - "8291965 600707.SH 20170103 9.12 0.9482 1.3437 \n", - "8291966 600708.SH 20170103 9.03 0.7694 1.0169 \n", - "8291967 600712.SH 20170103 10.29 0.5859 0.8028 \n", - "8291968 001872.SZ 20170103 19.33 1.0970 5.4258 \n", - "8291969 001914.SZ 20170103 12.37 3.2627 6.6991 \n", + "8372851 600708.SH 20170103 9.03 0.7694 1.0169 \n", + "8372852 600712.SH 20170103 10.29 0.5859 0.8028 \n", + "8372853 001872.SZ 20170103 19.33 1.0970 5.4258 \n", + "8372854 001914.SZ 20170103 12.37 3.2627 6.6991 \n", + "8372855 302132.SZ 20170103 23.28 0.4912 1.5149 \n", "\n", - " volume_ratio pe pe_ttm pb ps ps_ttm \\\n", - "0 0.87 NaN NaN 12.8888 2.9340 3.0625 \n", - "1 1.16 35.5101 15.2315 0.9534 0.3454 0.3402 \n", - "2 1.38 NaN NaN 15.2661 3.4220 4.2041 \n", - "3 0.85 48.9883 41.5405 2.2074 2.3641 2.3637 \n", - "4 0.69 NaN NaN 4.1442 3.7325 3.3186 \n", - "... ... ... ... ... ... ... \n", - "8291965 1.18 133.8070 35.6525 4.5692 28.6047 27.5926 \n", - "8291966 0.85 23.3367 22.2458 1.4847 0.9613 0.9248 \n", - "8291967 0.67 202.4855 287.1454 5.1852 2.3682 2.5386 \n", - "8291968 0.77 23.6158 23.1883 2.7052 6.6556 6.5584 \n", - "8291969 1.02 20.5631 15.1595 2.1186 1.4950 1.2600 \n", + " volume_ratio pe pe_ttm pb ps ps_ttm dv_ratio \\\n", + "0 0.98 65.2824 275.8084 3.0077 19.5374 20.5874 0.6618 \n", + "1 0.79 76.6697 70.6389 2.4679 13.2919 10.1678 NaN \n", + "2 1.53 15.1340 16.2489 1.4830 1.2425 1.1986 4.7619 \n", + "3 0.79 NaN NaN 9.0179 12.4251 19.5540 0.0000 \n", + "4 1.76 38.6030 47.7249 6.4916 3.7441 3.8039 1.6629 \n", + "... ... ... ... ... ... ... ... \n", + "8372851 0.85 23.3367 22.2458 1.4847 0.9613 0.9248 1.1074 \n", + "8372852 0.67 202.4855 287.1454 5.1852 2.3682 2.5386 0.1555 \n", + "8372853 0.77 23.6158 23.1883 2.7052 6.6556 6.5584 2.1211 \n", + "8372854 1.02 20.5631 15.1595 2.1186 1.4950 1.2600 0.4042 \n", + "8372855 0.74 91.3908 84.6980 6.9391 8.9531 8.8570 0.2291 \n", "\n", - " dv_ratio dv_ttm total_share float_share free_share total_mv \\\n", - "0 0.0000 NaN 114709.4532 104845.4958 79597.9456 5.769885e+05 \n", - "1 0.5633 0.5633 133684.4288 133684.4288 68397.8451 6.456958e+05 \n", - "2 0.0000 NaN 50493.6660 50493.6660 38088.2934 1.858167e+05 \n", - "3 0.8222 0.8222 26364.0000 20277.8618 14751.7331 2.565217e+05 \n", - "4 0.0000 NaN 14778.3896 10618.9439 8140.0483 3.384251e+05 \n", - "... ... ... ... ... ... ... \n", - "8291965 0.0000 NaN 73675.7688 73603.7688 51940.2015 6.719230e+05 \n", - "8291966 1.1074 1.1074 131871.9966 75088.9215 56812.2811 1.190804e+06 \n", - "8291967 0.1555 0.1555 54465.5360 53795.9475 39266.3119 5.604504e+05 \n", - "8291968 2.1211 2.1211 64476.3730 46486.6050 9398.8050 1.246328e+06 \n", - "8291969 0.4042 0.4042 66696.1416 66678.0666 32475.1786 8.250313e+05 \n", + " dv_ttm total_share float_share free_share total_mv \\\n", + "0 0.6618 12012.0000 6662.0170 4887.5170 3.267264e+05 \n", + "1 NaN 58337.8039 58337.8039 29040.9774 2.810132e+06 \n", + "2 4.7619 269409.0160 220844.2340 70621.6215 1.697277e+06 \n", + "3 NaN 76386.9228 76377.3438 68351.0115 5.568607e+05 \n", + "4 1.6629 56140.0000 56140.0000 11792.9916 1.008274e+06 \n", + "... ... ... ... ... ... \n", + "8372851 1.1074 131871.9966 75088.9215 56812.2811 1.190804e+06 \n", + "8372852 0.1555 54465.5360 53795.9475 39266.3119 5.604504e+05 \n", + "8372853 2.1211 64476.3730 46486.6050 9398.8050 1.246328e+06 \n", + "8372854 0.4042 66696.1416 66678.0666 32475.1786 8.250313e+05 \n", + "8372855 0.2291 39384.0333 30419.3588 9862.3809 9.168603e+05 \n", "\n", - " circ_mv is_st \n", - "0 527372.8439 False \n", - "1 645695.7911 False \n", - "2 185816.6909 True \n", - "3 197303.5953 False \n", - "4 243173.8153 False \n", - "... ... ... \n", - "8291965 671266.3715 False \n", - "8291966 678052.9611 False \n", - "8291967 553560.2998 False \n", - "8291968 898586.0747 False \n", - "8291969 824807.6838 False \n", + " circ_mv is_st \n", + "0 1.812069e+05 False \n", + "1 2.810132e+06 False \n", + "2 1.391319e+06 False \n", + "3 5.567908e+05 False \n", + "4 1.008274e+06 False \n", + "... ... ... \n", + "8372851 6.780530e+05 False \n", + "8372852 5.535603e+05 False \n", + "8372853 8.985861e+05 False \n", + "8372854 8.248077e+05 False \n", + "8372855 7.081627e+05 False \n", "\n", - "[8291970 rows x 19 columns]\n" + "[8372856 rows x 19 columns]\n" ] } ], - "execution_count": 6 + "execution_count": 4 }, { "cell_type": "code", "id": "2b58a8bf-ffc5-4482-8e4d-bf24da9277de", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T16:17:03.779399Z", - "start_time": "2025-02-11T16:15:26.662526Z" + "end_time": "2025-03-02T08:34:49.733727Z", + "start_time": "2025-03-02T08:33:16.498221Z" } }, "source": [ @@ -2219,15 +2257,15 @@ ] } ], - "execution_count": 7 + "execution_count": 5 }, { "cell_type": "code", "id": "57ac1d86-5ce8-4bc9-812f-b45dcc2a3b4c", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T16:17:03.857407Z", - "start_time": "2025-02-11T16:17:03.843423Z" + "end_time": "2025-03-02T08:34:49.778164Z", + "start_time": "2025-03-02T08:34:49.775512Z" } }, "source": [], diff --git a/code/data/name_change.ipynb b/code/data/name_change.ipynb index 8142f3d..367c985 100644 --- a/code/data/name_change.ipynb +++ b/code/data/name_change.ipynb @@ -5,8 +5,8 @@ "id": "94412ea8-aad7-47fb-8597-d80adef21a8b", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:41:07.699531Z", - "start_time": "2025-02-11T15:41:06.959399Z" + "end_time": "2025-03-01T09:19:24.564409Z", + "start_time": "2025-03-01T09:19:23.930364Z" } }, "source": [ @@ -23,8 +23,8 @@ "metadata": { "scrolled": true, "ExecuteTime": { - "end_time": "2025-02-11T16:18:37.728208Z", - "start_time": "2025-02-11T15:41:07.705536Z" + "end_time": "2025-03-01T09:56:42.369757Z", + "start_time": "2025-03-01T09:19:24.709524Z" } }, "source": [ @@ -228,7 +228,7 @@ "成功获取 000572.SZ 的数据\n", "成功获取 000573.SZ 的数据\n", "成功获取 000576.SZ 的数据\n", - "已调用300次API,等待 31.79 秒以满足速率限制...\n", + "已调用300次API,等待 41.14 秒以满足速率限制...\n", "成功获取 000581.SZ 的数据\n", "成功获取 000582.SZ 的数据\n", "成功获取 000584.SZ 的数据\n", @@ -379,7 +379,7 @@ "成功获取 000811.SZ 的数据\n", "成功获取 000812.SZ 的数据\n", "成功获取 000813.SZ 的数据\n", - "已调用300次API,等待 30.82 秒以满足速率限制...\n", + "已调用300次API,等待 40.78 秒以满足速率限制...\n", "成功获取 000815.SZ 的数据\n", "成功获取 000816.SZ 的数据\n", "成功获取 000818.SZ 的数据\n", @@ -530,7 +530,7 @@ "成功获取 001238.SZ 的数据\n", "成功获取 001239.SZ 的数据\n", "成功获取 001255.SZ 的数据\n", - "已调用300次API,等待 31.41 秒以满足速率限制...\n", + "已调用300次API,等待 40.77 秒以满足速率限制...\n", "成功获取 001256.SZ 的数据\n", "成功获取 001258.SZ 的数据\n", "成功获取 001259.SZ 的数据\n", @@ -681,7 +681,7 @@ "成功获取 002085.SZ 的数据\n", "成功获取 002086.SZ 的数据\n", "成功获取 002088.SZ 的数据\n", - "已调用300次API,等待 31.38 秒以满足速率限制...\n", + "已调用300次API,等待 40.70 秒以满足速率限制...\n", "成功获取 002090.SZ 的数据\n", "成功获取 002091.SZ 的数据\n", "成功获取 002092.SZ 的数据\n", @@ -832,7 +832,7 @@ "成功获取 002242.SZ 的数据\n", "成功获取 002243.SZ 的数据\n", "成功获取 002244.SZ 的数据\n", - "已调用300次API,等待 31.86 秒以满足速率限制...\n", + "已调用300次API,等待 40.20 秒以满足速率限制...\n", "成功获取 002245.SZ 的数据\n", "成功获取 002246.SZ 的数据\n", "成功获取 002247.SZ 的数据\n", @@ -983,7 +983,7 @@ "成功获取 002400.SZ 的数据\n", "成功获取 002401.SZ 的数据\n", "成功获取 002402.SZ 的数据\n", - "已调用300次API,等待 31.95 秒以满足速率限制...\n", + "已调用300次API,等待 40.84 秒以满足速率限制...\n", "成功获取 002403.SZ 的数据\n", "成功获取 002404.SZ 的数据\n", "成功获取 002405.SZ 的数据\n", @@ -1134,7 +1134,7 @@ "成功获取 002566.SZ 的数据\n", "成功获取 002567.SZ 的数据\n", "成功获取 002568.SZ 的数据\n", - "已调用300次API,等待 31.12 秒以满足速率限制...\n", + "已调用300次API,等待 41.66 秒以满足速率限制...\n", "成功获取 002569.SZ 的数据\n", "成功获取 002570.SZ 的数据\n", "成功获取 002571.SZ 的数据\n", @@ -1285,7 +1285,7 @@ "成功获取 002729.SZ 的数据\n", "成功获取 002730.SZ 的数据\n", "成功获取 002731.SZ 的数据\n", - "已调用300次API,等待 29.77 秒以满足速率限制...\n", + "已调用300次API,等待 40.74 秒以满足速率限制...\n", "成功获取 002732.SZ 的数据\n", "成功获取 002733.SZ 的数据\n", "成功获取 002734.SZ 的数据\n", @@ -1436,7 +1436,7 @@ "成功获取 002896.SZ 的数据\n", "成功获取 002897.SZ 的数据\n", "成功获取 002898.SZ 的数据\n", - "已调用300次API,等待 32.34 秒以满足速率限制...\n", + "已调用300次API,等待 41.14 秒以满足速率限制...\n", "成功获取 002899.SZ 的数据\n", "成功获取 002900.SZ 的数据\n", "成功获取 002901.SZ 的数据\n", @@ -1587,7 +1587,7 @@ "成功获取 300014.SZ 的数据\n", "成功获取 300015.SZ 的数据\n", "成功获取 300016.SZ 的数据\n", - "已调用300次API,等待 32.02 秒以满足速率限制...\n", + "已调用300次API,等待 40.57 秒以满足速率限制...\n", "成功获取 300017.SZ 的数据\n", "成功获取 300018.SZ 的数据\n", "成功获取 300019.SZ 的数据\n", @@ -1738,7 +1738,7 @@ "成功获取 300174.SZ 的数据\n", "成功获取 300175.SZ 的数据\n", "成功获取 300176.SZ 的数据\n", - "已调用300次API,等待 31.35 秒以满足速率限制...\n", + "已调用300次API,等待 41.05 秒以满足速率限制...\n", "成功获取 300177.SZ 的数据\n", "成功获取 300179.SZ 的数据\n", "成功获取 300180.SZ 的数据\n", @@ -1889,7 +1889,7 @@ "成功获取 300337.SZ 的数据\n", "成功获取 300338.SZ 的数据\n", "成功获取 300339.SZ 的数据\n", - "已调用300次API,等待 31.84 秒以满足速率限制...\n", + "已调用300次API,等待 40.69 秒以满足速率限制...\n", "成功获取 300340.SZ 的数据\n", "成功获取 300341.SZ 的数据\n", "成功获取 300342.SZ 的数据\n", @@ -2040,7 +2040,7 @@ "成功获取 300494.SZ 的数据\n", "成功获取 300496.SZ 的数据\n", "成功获取 300497.SZ 的数据\n", - "已调用300次API,等待 27.83 秒以满足速率限制...\n", + "已调用300次API,等待 40.51 秒以满足速率限制...\n", "成功获取 300498.SZ 的数据\n", "成功获取 300499.SZ 的数据\n", "成功获取 300500.SZ 的数据\n", @@ -2191,7 +2191,7 @@ "成功获取 300650.SZ 的数据\n", "成功获取 300651.SZ 的数据\n", "成功获取 300652.SZ 的数据\n", - "已调用300次API,等待 31.79 秒以满足速率限制...\n", + "已调用300次API,等待 39.15 秒以满足速率限制...\n", "成功获取 300653.SZ 的数据\n", "成功获取 300654.SZ 的数据\n", "成功获取 300655.SZ 的数据\n", @@ -2342,7 +2342,7 @@ "成功获取 300810.SZ 的数据\n", "成功获取 300811.SZ 的数据\n", "成功获取 300812.SZ 的数据\n", - "已调用300次API,等待 31.09 秒以满足速率限制...\n", + "已调用300次API,等待 38.87 秒以满足速率限制...\n", "成功获取 300813.SZ 的数据\n", "成功获取 300814.SZ 的数据\n", "成功获取 300815.SZ 的数据\n", @@ -2493,7 +2493,7 @@ "成功获取 300966.SZ 的数据\n", "成功获取 300967.SZ 的数据\n", "成功获取 300968.SZ 的数据\n", - "已调用300次API,等待 31.39 秒以满足速率限制...\n", + "已调用300次API,等待 40.54 秒以满足速率限制...\n", "成功获取 300969.SZ 的数据\n", "成功获取 300970.SZ 的数据\n", "成功获取 300971.SZ 的数据\n", @@ -2644,7 +2644,7 @@ "成功获取 301128.SZ 的数据\n", "成功获取 301129.SZ 的数据\n", "成功获取 301130.SZ 的数据\n", - "已调用300次API,等待 31.44 秒以满足速率限制...\n", + "已调用300次API,等待 41.03 秒以满足速率限制...\n", "成功获取 301131.SZ 的数据\n", "成功获取 301132.SZ 的数据\n", "成功获取 301133.SZ 的数据\n", @@ -2795,7 +2795,7 @@ "成功获取 301313.SZ 的数据\n", "成功获取 301314.SZ 的数据\n", "成功获取 301315.SZ 的数据\n", - "已调用300次API,等待 31.43 秒以满足速率限制...\n", + "已调用300次API,等待 40.99 秒以满足速率限制...\n", "成功获取 301316.SZ 的数据\n", "成功获取 301317.SZ 的数据\n", "成功获取 301318.SZ 的数据\n", @@ -2946,7 +2946,7 @@ "成功获取 301618.SZ 的数据\n", "成功获取 301622.SZ 的数据\n", "成功获取 301626.SZ 的数据\n", - "已调用300次API,等待 31.51 秒以满足速率限制...\n", + "已调用300次API,等待 41.17 秒以满足速率限制...\n", "成功获取 301628.SZ 的数据\n", "成功获取 301631.SZ 的数据\n", "成功获取 301633.SZ 的数据\n", @@ -3097,7 +3097,7 @@ "成功获取 600170.SH 的数据\n", "成功获取 600171.SH 的数据\n", "成功获取 600172.SH 的数据\n", - "已调用300次API,等待 31.08 秒以满足速率限制...\n", + "已调用300次API,等待 40.74 秒以满足速率限制...\n", "成功获取 600173.SH 的数据\n", "成功获取 600176.SH 的数据\n", "成功获取 600177.SH 的数据\n", @@ -3248,7 +3248,7 @@ "成功获取 600366.SH 的数据\n", "成功获取 600367.SH 的数据\n", "成功获取 600368.SH 的数据\n", - "已调用300次API,等待 32.11 秒以满足速率限制...\n", + "已调用300次API,等待 41.16 秒以满足速率限制...\n", "成功获取 600369.SH 的数据\n", "成功获取 600370.SH 的数据\n", "成功获取 600371.SH 的数据\n", @@ -3399,7 +3399,7 @@ "成功获取 600572.SH 的数据\n", "成功获取 600573.SH 的数据\n", "成功获取 600575.SH 的数据\n", - "已调用300次API,等待 32.27 秒以满足速率限制...\n", + "已调用300次API,等待 40.45 秒以满足速率限制...\n", "成功获取 600576.SH 的数据\n", "成功获取 600577.SH 的数据\n", "成功获取 600578.SH 的数据\n", @@ -3550,7 +3550,7 @@ "成功获取 600748.SH 的数据\n", "成功获取 600749.SH 的数据\n", "成功获取 600750.SH 的数据\n", - "已调用300次API,等待 30.57 秒以满足速率限制...\n", + "已调用300次API,等待 41.00 秒以满足速率限制...\n", "成功获取 600751.SH 的数据\n", "成功获取 600753.SH 的数据\n", "成功获取 600754.SH 的数据\n", @@ -3701,7 +3701,7 @@ "成功获取 600956.SH 的数据\n", "成功获取 600958.SH 的数据\n", "成功获取 600959.SH 的数据\n", - "已调用300次API,等待 29.71 秒以满足速率限制...\n", + "已调用300次API,等待 41.08 秒以满足速率限制...\n", "成功获取 600960.SH 的数据\n", "成功获取 600961.SH 的数据\n", "成功获取 600962.SH 的数据\n", @@ -3852,7 +3852,7 @@ "成功获取 601519.SH 的数据\n", "成功获取 601528.SH 的数据\n", "成功获取 601555.SH 的数据\n", - "已调用300次API,等待 32.29 秒以满足速率限制...\n", + "已调用300次API,等待 41.02 秒以满足速率限制...\n", "成功获取 601566.SH 的数据\n", "成功获取 601567.SH 的数据\n", "成功获取 601568.SH 的数据\n", @@ -4003,7 +4003,7 @@ "成功获取 603041.SH 的数据\n", "成功获取 603042.SH 的数据\n", "成功获取 603043.SH 的数据\n", - "已调用300次API,等待 30.99 秒以满足速率限制...\n", + "已调用300次API,等待 40.67 秒以满足速率限制...\n", "成功获取 603045.SH 的数据\n", "成功获取 603048.SH 的数据\n", "成功获取 603050.SH 的数据\n", @@ -4154,7 +4154,7 @@ "成功获取 603228.SH 的数据\n", "成功获取 603229.SH 的数据\n", "成功获取 603230.SH 的数据\n", - "已调用300次API,等待 30.34 秒以满足速率限制...\n", + "已调用300次API,等待 41.24 秒以满足速率限制...\n", "成功获取 603231.SH 的数据\n", "成功获取 603232.SH 的数据\n", "成功获取 603233.SH 的数据\n", @@ -4305,7 +4305,7 @@ "成功获取 603530.SH 的数据\n", "成功获取 603533.SH 的数据\n", "成功获取 603535.SH 的数据\n", - "已调用300次API,等待 30.84 秒以满足速率限制...\n", + "已调用300次API,等待 40.73 秒以满足速率限制...\n", "成功获取 603536.SH 的数据\n", "成功获取 603538.SH 的数据\n", "成功获取 603551.SH 的数据\n", @@ -4456,7 +4456,7 @@ "成功获取 603819.SH 的数据\n", "成功获取 603822.SH 的数据\n", "成功获取 603823.SH 的数据\n", - "已调用300次API,等待 30.10 秒以满足速率限制...\n", + "已调用300次API,等待 41.30 秒以满足速率限制...\n", "成功获取 603825.SH 的数据\n", "成功获取 603826.SH 的数据\n", "成功获取 603828.SH 的数据\n", @@ -4607,7 +4607,7 @@ "成功获取 605167.SH 的数据\n", "成功获取 605168.SH 的数据\n", "成功获取 605169.SH 的数据\n", - "已调用300次API,等待 32.36 秒以满足速率限制...\n", + "已调用300次API,等待 40.75 秒以满足速率限制...\n", "成功获取 605177.SH 的数据\n", "成功获取 605178.SH 的数据\n", "成功获取 605179.SH 的数据\n", @@ -4758,7 +4758,7 @@ "成功获取 688097.SH 的数据\n", "成功获取 688098.SH 的数据\n", "成功获取 688099.SH 的数据\n", - "已调用300次API,等待 30.11 秒以满足速率限制...\n", + "已调用300次API,等待 41.17 秒以满足速率限制...\n", "成功获取 688100.SH 的数据\n", "成功获取 688101.SH 的数据\n", "成功获取 688102.SH 的数据\n", @@ -4909,7 +4909,7 @@ "成功获取 688271.SH 的数据\n", "成功获取 688272.SH 的数据\n", "成功获取 688273.SH 的数据\n", - "已调用300次API,等待 32.68 秒以满足速率限制...\n", + "已调用300次API,等待 41.28 秒以满足速率限制...\n", "成功获取 688275.SH 的数据\n", "成功获取 688276.SH 的数据\n", "成功获取 688277.SH 的数据\n", @@ -5060,7 +5060,7 @@ "成功获取 688486.SH 的数据\n", "成功获取 688488.SH 的数据\n", "成功获取 688489.SH 的数据\n", - "已调用300次API,等待 30.08 秒以满足速率限制...\n", + "已调用300次API,等待 41.23 秒以满足速率限制...\n", "成功获取 688496.SH 的数据\n", "成功获取 688498.SH 的数据\n", "成功获取 688499.SH 的数据\n", @@ -5211,7 +5211,7 @@ "成功获取 688689.SH 的数据\n", "成功获取 688690.SH 的数据\n", "成功获取 688691.SH 的数据\n", - "已调用300次API,等待 29.34 秒以满足速率限制...\n", + "已调用300次API,等待 40.17 秒以满足速率限制...\n", "成功获取 688692.SH 的数据\n", "成功获取 688693.SH 的数据\n", "成功获取 688695.SH 的数据\n", @@ -5362,7 +5362,7 @@ "成功获取 835184.BJ 的数据\n", "成功获取 835185.BJ 的数据\n", "成功获取 835207.BJ 的数据\n", - "已调用300次API,等待 31.92 秒以满足速率限制...\n", + "已调用300次API,等待 41.36 秒以满足速率限制...\n", "成功获取 835237.BJ 的数据\n", "成功获取 835305.BJ 的数据\n", "成功获取 835368.BJ 的数据\n", @@ -5513,7 +5513,7 @@ "成功获取 000005.SZ 的数据\n", "成功获取 000013.SZ 的数据\n", "成功获取 000015.SZ 的数据\n", - "已调用300次API,等待 31.12 秒以满足速率限制...\n", + "已调用300次API,等待 40.98 秒以满足速率限制...\n", "成功获取 000018.SZ 的数据\n", "成功获取 000023.SZ 的数据\n", "成功获取 000024.SZ 的数据\n", @@ -5615,14 +5615,14 @@ "成功获取 002341.SZ 的数据\n", "成功获取 002359.SZ 的数据\n", "成功获取 002411.SZ 的数据\n", - "成功获取 002450.SZ 的数据\n", - "成功获取 002464.SZ 的数据\n", - "成功获取 002473.SZ 的数据\n", - "成功获取 002477.SZ 的数据\n", "成功获取 002417.SZ 的数据\n", "成功获取 002433.SZ 的数据\n", "成功获取 002435.SZ 的数据\n", "成功获取 002447.SZ 的数据\n", + "成功获取 002450.SZ 的数据\n", + "成功获取 002464.SZ 的数据\n", + "成功获取 002473.SZ 的数据\n", + "成功获取 002477.SZ 的数据\n", "成功获取 002499.SZ 的数据\n", "成功获取 002502.SZ 的数据\n", "成功获取 002503.SZ 的数据\n", @@ -5664,7 +5664,7 @@ "成功获取 300309.SZ 的数据\n", "成功获取 300312.SZ 的数据\n", "成功获取 300325.SZ 的数据\n", - "已调用300次API,等待 32.31 秒以满足速率限制...\n", + "已调用300次API,等待 40.90 秒以满足速率限制...\n", "成功获取 300330.SZ 的数据\n", "成功获取 300336.SZ 的数据\n", "成功获取 300356.SZ 的数据\n", @@ -5806,7 +5806,7 @@ "2 000001.SZ 深发展A 20070620 20120801 完成股改\n", "3 000001.SZ 深发展A 20070620 20120801 完成股改\n", "4 000001.SZ S深发展A 20061009 20070619 未股改加S\n", - "名称变化记录总数: 31891\n" + "名称变化记录总数: 31934\n" ] } ], @@ -5817,8 +5817,8 @@ "id": "4d5524b8-2a90-44bb-b5ef-e59cfa232ff0", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T16:18:37.959516Z", - "start_time": "2025-02-11T16:18:37.821725Z" + "end_time": "2025-03-01T09:56:42.543882Z", + "start_time": "2025-03-01T09:56:42.431891Z" } }, "source": [ @@ -5845,8 +5845,8 @@ "id": "1e920791-e8de-4a51-a39b-283f54132b44", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T16:18:37.974954Z", - "start_time": "2025-02-11T16:18:37.964501Z" + "end_time": "2025-03-01T09:56:42.552436Z", + "start_time": "2025-03-01T09:56:42.545392Z" } }, "source": [ @@ -5873,8 +5873,8 @@ "id": "4f5651f7-0910-4df5-9c3f-79d6ce033d53", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T16:18:38.006078Z", - "start_time": "2025-02-11T16:18:37.991781Z" + "end_time": "2025-03-01T09:56:42.579674Z", + "start_time": "2025-03-01T09:56:42.569013Z" } }, "source": [], diff --git a/code/data/update/update_daily_basic.ipynb b/code/data/update/update_daily_basic.ipynb index 7931fe5..db50192 100644 --- a/code/data/update/update_daily_basic.ipynb +++ b/code/data/update/update_daily_basic.ipynb @@ -2,35 +2,49 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, "id": "18d1d622-b083-4cc4-a6f8-7c1ed2d0edd2", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:43:54.745322Z", - "start_time": "2025-02-11T15:43:53.837662Z" + "end_time": "2025-03-30T16:42:34.194992Z", + "start_time": "2025-03-30T16:42:33.440178Z" } }, - "outputs": [], "source": [ "import tushare as ts\n", "ts.set_token('3a0741c702ee7e5e5f2bf1f0846bafaafe4e320833240b2a7e4a685f')\n", "pro = ts.pro_api()" - ] + ], + "outputs": [], + "execution_count": 1 }, { "cell_type": "code", - "execution_count": 2, "id": "14671a7f72de2564", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:53:08.235573Z", - "start_time": "2025-02-11T15:53:07.753701Z" + "end_time": "2025-03-30T16:42:36.432691Z", + "start_time": "2025-03-30T16:42:34.197998Z" } }, - "outputs": [], "source": [ "from datetime import datetime\n", "import pandas as pd\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "def filter_rows(df):\n", + " # 按照 name 和 start_date 分组\n", + " def select_row(group):\n", + " # 如果有 end_date 不为 NaT 的行,优先保留这些行\n", + " valid_rows = group[group['end_date'].notna()]\n", + " if not valid_rows.empty:\n", + " return valid_rows.iloc[0] # 返回第一个有效行\n", + " else:\n", + " return group.iloc[0] # 如果没有有效行,返回第一行\n", + "\n", + " filtered_df = df.groupby(['name', 'start_date'], group_keys=False).apply(select_row)\n", + " filtered_df = filtered_df.reset_index(drop=True)\n", + " return filtered_df\n", "\n", "def is_st(name_change_dict, stock_code, target_date):\n", " target_date = datetime.strptime(target_date, '%Y%m%d')\n", @@ -58,39 +72,20 @@ " # 只保留 'ST' 和 '*ST' 的记录\n", " st_data = group[(group['change_reason'] == 'ST') | (group['change_reason'] == '*ST')]\n", " if not st_data.empty:\n", - " name_change_dict[ts_code] = st_data" - ] + " name_change_dict[ts_code] = filter_rows(st_data)" + ], + "outputs": [], + "execution_count": 2 }, { "cell_type": "code", - "execution_count": 3, "id": "e7f8cce2f80e2f20", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:53:19.812860Z", - "start_time": "2025-02-11T15:53:09.614377Z" + "end_time": "2025-03-30T16:43:03.790361Z", + "start_time": "2025-03-30T16:42:36.633554Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Index: 8291970 entries, 0 to 8291969\n", - "Data columns (total 2 columns):\n", - " # Column Dtype \n", - "--- ------ ----- \n", - " 0 ts_code object\n", - " 1 trade_date object\n", - "dtypes: object(2)\n", - "memory usage: 189.8+ MB\n", - "None\n", - "20250211\n", - "20250212\n" - ] - } - ], "source": [ "import time\n", "from concurrent.futures import ThreadPoolExecutor, as_completed\n", @@ -104,39 +99,44 @@ " max_date = df['trade_date'].max()\n", "\n", "print(max_date)\n", - "trade_cal = pro.trade_cal(exchange='', start_date='20170101', end_date='20250220')\n", + "trade_cal = pro.trade_cal(exchange='', start_date='20170101', end_date='20250420')\n", "trade_cal = trade_cal[trade_cal['is_open'] == 1] # 只保留交易日\n", "trade_dates = trade_cal[trade_cal['cal_date'] > max_date]['cal_date'].tolist()\n", "start_date = min(trade_dates)\n", "print(start_date)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "553cfb36-f560-4cc4-b2bc-68323ccc5072", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-11T15:53:24.100612Z", - "start_time": "2025-02-11T15:53:22.361257Z" - }, - "scrolled": true - }, + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "任务 20250220 完成\n", - "任务 20250219 完成\n", - "任务 20250217 完成\n", - "任务 20250218 完成\n", - "任务 20250214 完成\n", - "任务 20250213 完成\n", - "任务 20250212 完成\n" + "\n", + "Index: 8453605 entries, 0 to 32308\n", + "Data columns (total 2 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object\n", + " 1 trade_date object\n", + "dtypes: object(2)\n", + "memory usage: 193.5+ MB\n", + "None\n", + "20250321\n", + "20250324\n" ] } ], + "execution_count": 3 + }, + { + "cell_type": "code", + "id": "553cfb36-f560-4cc4-b2bc-68323ccc5072", + "metadata": { + "scrolled": true, + "ExecuteTime": { + "end_time": "2025-03-30T16:43:07.947442Z", + "start_time": "2025-03-30T16:43:03.827519Z" + } + }, "source": [ "\n", "\n", @@ -186,169 +186,202 @@ " # 重置批次起始时间\n", " batch_start_time = time.time()\n", "\n" - ] + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "任务 20250418 完成\n", + "任务 20250417 完成\n", + "任务 20250416 完成\n", + "任务 20250415 完成\n", + "任务 20250411 完成\n", + "任务 20250414 完成\n", + "任务 20250410 完成\n", + "任务 20250409 完成\n", + "任务 20250408 完成\n", + "任务 20250407 完成\n", + "任务 20250403 完成\n", + "任务 20250402 完成\n", + "任务 20250331 完成\n", + "任务 20250401 完成\n", + "任务 20250327 完成\n", + "任务 20250328 完成\n", + "任务 20250326 完成\n", + "任务 20250324 完成\n", + "任务 20250325 完成\n" + ] + } + ], + "execution_count": 4 }, { "cell_type": "code", - "execution_count": 5, "id": "919023c693d7a47a", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:53:25.913933Z", - "start_time": "2025-02-11T15:53:25.902629Z" + "end_time": "2025-03-30T16:43:07.962318Z", + "start_time": "2025-03-30T16:43:07.951757Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " ts_code trade_date close turnover_rate turnover_rate_f \\\n", - "0 601162.SH 20250212 4.77 7.3760 9.7054 \n", - "1 603216.SH 20250212 11.42 8.8711 8.8711 \n", - "2 872808.BJ 20250212 74.36 4.1219 15.3296 \n", - "3 601881.SH 20250212 14.43 0.5617 1.9533 \n", - "4 002837.SZ 20250212 42.25 3.8199 5.7136 \n", - "... ... ... ... ... ... \n", - "5380 603931.SH 20250212 23.83 1.4692 4.6843 \n", - "5381 688567.SH 20250212 12.35 1.3091 2.1970 \n", - "5382 688530.SH 20250212 19.30 6.6093 6.6093 \n", - "5383 301363.SZ 20250212 31.99 2.1990 2.1990 \n", - "5384 833533.BJ 20250212 46.02 27.7269 27.7597 \n", - "\n", - " volume_ratio pe pe_ttm pb ps ps_ttm dv_ratio \\\n", - "0 2.00 134.5633 NaN 1.7935 12.0634 19.0461 0.0000 \n", - "1 2.09 26.5657 27.5224 1.4454 1.9304 1.9996 2.6270 \n", - "2 1.20 142.3485 196.0315 22.9124 22.8711 25.8281 NaN \n", - "3 0.84 20.0264 15.5707 1.4245 4.6898 4.4609 2.1067 \n", - "4 0.65 91.3544 64.5935 11.2259 8.9056 7.2600 0.3621 \n", - "... ... ... ... ... ... ... ... \n", - "5380 1.16 27.1631 29.0662 3.0982 6.8392 6.9124 1.1120 \n", - "5381 1.01 NaN NaN 1.4955 0.9183 1.0469 NaN \n", - "5382 0.99 62.5995 198.4906 3.6879 6.4857 7.9319 NaN \n", - "5383 0.98 41.5226 47.9900 3.8396 9.7258 8.9664 0.4982 \n", - "5384 0.84 52.3997 62.1858 13.3582 6.6261 5.9638 NaN \n", - "\n", - " dv_ttm total_share float_share free_share total_mv \\\n", - "0 NaN 8.665757e+05 866575.7464 658594.7570 4.133566e+06 \n", - "1 2.6270 2.226900e+04 5669.0000 5669.0000 2.543120e+05 \n", - "2 NaN 2.000000e+04 19461.9464 5233.0650 1.487200e+06 \n", - "3 2.1067 1.093440e+06 724341.7623 208280.6759 1.577834e+07 \n", - "4 0.3621 7.438227e+04 64662.2002 43230.4691 3.142651e+06 \n", - "... ... ... ... ... ... \n", - "5380 1.1120 1.995584e+04 19955.8380 6258.8392 4.755476e+05 \n", - "5381 NaN 1.222104e+05 122210.3885 72818.9706 1.509298e+06 \n", - "5382 NaN 1.600448e+04 3200.8966 3200.8966 3.088865e+05 \n", - "5383 0.4982 4.066600e+04 11215.9100 11215.9100 1.300905e+06 \n", - "5384 NaN 1.005826e+04 3796.0235 3791.5280 4.628809e+05 \n", - "\n", - " circ_mv is_st \n", - "0 4.133566e+06 False \n", - "1 6.473998e+04 False \n", - "2 1.447190e+06 False \n", - "3 1.045225e+07 False \n", - "4 2.731978e+06 False \n", - "... ... ... \n", - "5380 4.755476e+05 False \n", - "5381 1.509298e+06 False \n", - "5382 6.177730e+04 False \n", - "5383 3.587970e+05 False \n", - "5384 1.746930e+05 False \n", - "\n", - "[5385 rows x 19 columns]\n" - ] - } - ], "source": [ "all_daily_data_df = pd.concat(all_daily_data, ignore_index=True)\n", "print(all_daily_data_df)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "28cb78d032671b20", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-11T15:53:42.062142Z", - "start_time": "2025-02-11T15:53:42.044324Z" - } - }, + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " ts_code trade_date close turnover_rate turnover_rate_f \\\n", - "10 002366.SZ 20250212 5.10 3.8029 4.1742 \n", - "48 002124.SZ 20250212 2.80 1.8388 1.9195 \n", - "57 000504.SZ 20250212 9.32 0.9666 1.5370 \n", - "63 603007.SH 20250212 10.03 2.0477 2.7581 \n", - "91 300201.SZ 20250212 5.33 2.3317 3.1604 \n", - "... ... ... ... ... ... \n", - "5303 002316.SZ 20250212 3.52 3.1023 3.3580 \n", - "5335 600568.SH 20250212 1.30 0.3996 0.6514 \n", - "5364 002168.SZ 20250212 2.48 0.8869 1.0824 \n", - "5367 300600.SZ 20250212 7.19 0.7517 1.4024 \n", - "5369 000972.SZ 20250212 3.38 4.6979 7.2993 \n", + " ts_code trade_date close turnover_rate turnover_rate_f \\\n", + "0 603328.SH 20250327 10.44 1.0910 2.6596 \n", + "1 603989.SH 20250327 15.66 0.9036 2.6145 \n", + "2 603194.SH 20250327 38.03 14.0348 14.0348 \n", + "3 600884.SH 20250327 7.13 1.9769 2.1153 \n", + "4 688325.SH 20250327 47.26 1.5250 1.8078 \n", + "... ... ... ... ... ... \n", + "26946 688539.SH 20250325 26.70 1.0257 1.3011 \n", + "26947 688479.SH 20250325 18.73 0.9840 1.2588 \n", + "26948 000552.SZ 20250325 2.63 1.8147 3.0665 \n", + "26949 688719.SH 20250325 31.64 4.2998 5.1737 \n", + "26950 002709.SZ 20250325 19.50 1.2468 1.4268 \n", "\n", - " volume_ratio pe pe_ttm pb ps ps_ttm dv_ratio \\\n", - "10 0.92 52.0324 56.8856 2.2889 14.2486 11.9214 0.0000 \n", - "48 0.97 NaN 260.7218 1.7484 0.6080 0.6154 0.0000 \n", - "57 0.83 NaN NaN 12.3702 22.4855 24.7156 0.0000 \n", - "63 0.86 NaN NaN 24.6750 55.2244 76.4853 0.0000 \n", - "91 0.75 26.1255 26.1088 4.2311 3.9774 4.2028 0.6431 \n", - "... ... ... ... ... ... ... ... \n", - "5303 0.95 NaN NaN 19.4146 2.2930 2.3153 0.0000 \n", - "5335 0.76 NaN NaN 1.1378 4.0571 4.0379 0.0000 \n", - "5364 0.88 1024.9794 NaN NaN 7.6515 7.4299 0.0000 \n", - "5367 1.18 NaN NaN 2.2914 10.7845 8.9952 0.0000 \n", - "5369 0.77 24.0853 120.2360 16.2931 4.5277 4.9137 0.0000 \n", + " volume_ratio pe pe_ttm pb ps ps_ttm dv_ratio \\\n", + "0 0.79 29.3625 23.3887 2.5786 3.2807 2.9727 1.8582 \n", + "1 0.79 17.8968 27.7940 1.7060 1.8591 1.6666 1.6823 \n", + "2 1.87 18.9266 18.3213 3.2891 2.5755 2.4322 NaN \n", + "3 0.52 20.9930 NaN 0.7305 0.8425 0.9106 2.7224 \n", + "4 0.93 67.1638 50.1073 2.3433 16.1029 10.2149 NaN \n", + "... ... ... ... ... ... ... ... \n", + "26946 0.56 51.5254 83.3548 2.8475 14.5500 13.9718 NaN \n", + "26947 0.61 23.5448 33.4921 1.4043 3.6736 4.5444 NaN \n", + "26948 1.42 8.0989 11.6324 0.8431 1.2501 1.3463 3.8023 \n", + "26949 1.64 26.3323 49.9921 2.0474 4.4195 3.6954 NaN \n", + "26950 0.76 19.7447 78.2248 2.9106 2.4233 3.0741 1.5444 \n", "\n", - " dv_ttm total_share float_share free_share total_mv \\\n", - "10 NaN 208093.7640 125646.4390 114472.2056 1.061278e+06 \n", - "48 NaN 222193.3832 197428.3498 189130.4452 6.221415e+05 \n", - "57 NaN 33002.3098 31066.8701 19536.7046 3.075815e+05 \n", - "63 NaN 87689.6101 49983.0778 37108.5778 8.795268e+05 \n", - "91 0.6431 100904.3607 100450.7422 74110.3317 5.378202e+05 \n", - "... ... ... ... ... ... \n", - "5303 NaN 39312.0000 31500.7500 29101.6694 1.383782e+05 \n", - "5335 NaN 199286.9681 166906.7279 102374.4773 2.590731e+05 \n", - "5364 NaN 78416.3368 78416.3368 64258.0991 1.944725e+05 \n", - "5367 NaN 29423.4480 24616.3436 13195.4382 2.115546e+05 \n", - "5369 NaN 77128.3579 77128.3579 49641.0760 2.606938e+05 \n", + " dv_ttm total_share float_share free_share total_mv \\\n", + "0 1.8582 99844.2611 99844.2611 40955.5563 1.042374e+06 \n", + "1 1.6823 40113.0603 40113.0603 13863.2102 6.281705e+05 \n", + "2 NaN 40100.0000 4982.8436 4982.8436 1.525003e+06 \n", + "3 2.7224 225339.6168 175723.6492 164220.4548 1.606671e+06 \n", + "4 NaN 8494.7740 3830.4117 3231.0886 4.014630e+05 \n", + "... ... ... ... ... ... \n", + "26946 NaN 18592.0000 10286.0800 8109.0800 4.964064e+05 \n", + "26947 NaN 14431.7400 6087.4224 4758.2224 2.703065e+05 \n", + "26948 3.8023 535180.1936 372577.7383 220477.9354 1.407524e+06 \n", + "26949 NaN 11538.5418 7349.9938 6108.5305 3.650795e+05 \n", + "26950 1.5444 191434.3762 138501.6891 121034.9868 3.732970e+06 \n", "\n", - " circ_mv is_st \n", - "10 640796.8389 True \n", - "48 552799.3794 True \n", - "57 289543.2293 True \n", - "63 501330.2703 True \n", - "91 535402.4559 True \n", - "... ... ... \n", - "5303 110882.6400 True \n", - "5335 216978.7463 True \n", - "5364 194472.5153 True \n", - "5367 176991.5105 True \n", - "5369 260693.8497 True \n", + " circ_mv is_st \n", + "0 1.042374e+06 False \n", + "1 6.281705e+05 False \n", + "2 1.894975e+05 False \n", + "3 1.252910e+06 False \n", + "4 1.810253e+05 False \n", + "... ... ... \n", + "26946 2.746383e+05 False \n", + "26947 1.140174e+05 False \n", + "26948 9.798795e+05 False \n", + "26949 2.325538e+05 False \n", + "26950 2.700783e+06 False \n", "\n", - "[318 rows x 19 columns]\n" + "[26951 rows x 19 columns]\n" ] } ], - "source": [ - "print(all_daily_data_df[all_daily_data_df['is_st']])" - ] + "execution_count": 5 + }, + { + "cell_type": "code", + "id": "28cb78d032671b20", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-30T16:43:08.000073Z", + "start_time": "2025-03-30T16:43:07.984082Z" + } + }, + "source": [ + "print(all_daily_data_df[all_daily_data_df['is_st']])" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ts_code trade_date close turnover_rate turnover_rate_f \\\n", + "100 002528.SZ 20250327 2.53 0.6855 1.4642 \n", + "128 300163.SZ 20250327 3.15 3.0563 3.2999 \n", + "129 300205.SZ 20250327 4.34 0.9211 1.5246 \n", + "147 000851.SZ 20250327 2.53 2.2990 2.6472 \n", + "299 300097.SZ 20250327 4.88 3.1648 3.6912 \n", + "... ... ... ... ... ... \n", + "26750 000506.SZ 20250325 5.21 1.2689 1.8939 \n", + "26770 002592.SZ 20250325 5.22 1.0547 1.6712 \n", + "26786 600603.SH 20250325 7.63 0.4610 1.0776 \n", + "26828 002528.SZ 20250325 2.51 0.9799 2.0928 \n", + "26906 300097.SZ 20250325 4.92 3.2717 3.8159 \n", + "\n", + " volume_ratio pe pe_ttm pb ps ps_ttm dv_ratio \\\n", + "100 0.43 NaN NaN 7.3528 2.1714 2.7257 0.0000 \n", + "128 0.87 NaN NaN 3.0547 5.9187 5.8999 0.0000 \n", + "129 0.63 94.7108 NaN 1.3743 1.0976 1.5538 0.4608 \n", + "147 0.64 NaN NaN 1.0360 0.4939 0.8666 0.0000 \n", + "299 0.70 10.0614 NaN 2.2055 2.9549 3.1999 0.0000 \n", + "... ... ... ... ... ... ... ... \n", + "26750 0.37 725.4828 NaN 8.2869 17.0204 21.9262 0.0000 \n", + "26770 0.94 14.0192 61.1217 1.6387 2.7253 2.3121 0.0000 \n", + "26786 0.56 15.6086 24.2223 1.3160 1.8461 2.4398 0.0000 \n", + "26828 0.58 NaN NaN 7.2947 2.1542 2.7042 0.0000 \n", + "26906 0.53 10.1438 NaN 2.2236 2.9791 3.2261 0.0000 \n", + "\n", + " dv_ttm total_share float_share free_share total_mv circ_mv \\\n", + "100 NaN 119867.5082 105021.9577 49171.2582 303264.7957 265705.5530 \n", + "128 NaN 47400.0000 41596.4553 38525.5904 149310.0000 131028.8342 \n", + "129 0.4608 43005.6000 42599.1218 25737.4813 186644.3040 184880.1886 \n", + "147 NaN 115786.0020 113197.7266 98311.5254 292938.5851 286390.2483 \n", + "299 NaN 28854.9669 27000.9948 23150.5534 140812.2385 131764.8546 \n", + "... ... ... ... ... ... ... \n", + "26750 NaN 92901.7761 92867.0961 62218.8027 484018.2535 483837.5707 \n", + "26770 NaN 28333.1157 26271.6370 16580.1814 147898.8640 137137.9451 \n", + "26786 NaN 119332.9151 119332.9151 51048.6002 910510.1422 910510.1422 \n", + "26828 NaN 119867.5082 105021.9577 49171.2582 300867.4456 263605.1138 \n", + "26906 NaN 28854.9669 27000.9948 23150.5534 141966.4371 132844.8944 \n", + "\n", + " is_st \n", + "100 True \n", + "128 True \n", + "129 True \n", + "147 True \n", + "299 True \n", + "... ... \n", + "26750 True \n", + "26770 True \n", + "26786 True \n", + "26828 True \n", + "26906 True \n", + "\n", + "[540 rows x 19 columns]\n" + ] + } + ], + "execution_count": 6 }, { "cell_type": "code", - "execution_count": 7, "id": "692b58674b7462c9", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:53:33.693894Z", - "start_time": "2025-02-11T15:53:33.609884Z" + "end_time": "2025-03-30T16:43:08.703938Z", + "start_time": "2025-03-30T16:43:08.021067Z" } }, + "source": [ + "# 将数据保存为 HDF5 文件(table 格式)\n", + "all_daily_data_df.to_hdf(h5_filename, key='daily_basic', mode='a', format='table', append=True, data_columns=True)\n", + "\n", + "print(\"所有每日基础数据获取并保存完毕!\")\n" + ], "outputs": [ { "name": "stdout", @@ -358,30 +391,29 @@ ] } ], - "source": [ - "# 将数据保存为 HDF5 文件(table 格式)\n", - "all_daily_data_df.to_hdf(h5_filename, key='daily_basic', mode='a', format='table', append=True, data_columns=True)\n", - "\n", - "print(\"所有每日基础数据获取并保存完毕!\")\n" - ] + "execution_count": 7 }, { "cell_type": "code", - "execution_count": 8, "id": "d7a773fc20293477", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:54:27.868021Z", - "start_time": "2025-02-11T15:54:18.853803Z" + "end_time": "2025-03-30T16:43:15.188800Z", + "start_time": "2025-03-30T16:43:08.725449Z" } }, + "source": [ + "with pd.HDFStore(h5_filename, mode='r') as store:\n", + " df = store[key][['ts_code', 'trade_date', 'is_st']]\n", + " print(df.info())" + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", - "Index: 8297355 entries, 0 to 5384\n", + "Index: 8480556 entries, 0 to 26950\n", "Data columns (total 3 columns):\n", " # Column Dtype \n", "--- ------ ----- \n", @@ -389,16 +421,12 @@ " 1 trade_date object\n", " 2 is_st bool \n", "dtypes: bool(1), object(2)\n", - "memory usage: 197.8+ MB\n", + "memory usage: 202.2+ MB\n", "None\n" ] } ], - "source": [ - "with pd.HDFStore(h5_filename, mode='r') as store:\n", - " df = store[key][['ts_code', 'trade_date', 'is_st']]\n", - " print(df.info())" - ] + "execution_count": 8 } ], "metadata": { @@ -417,7 +445,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.19" + "version": "3.11.11" } }, "nbformat": 4, diff --git a/code/data/update/update_daily_data.ipynb b/code/data/update/update_daily_data.ipynb index 3842626..b1b40c0 100644 --- a/code/data/update/update_daily_data.ipynb +++ b/code/data/update/update_daily_data.ipynb @@ -2,54 +2,35 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, "id": "f294ba92-512a-48e6-bbaa-e19401c691ba", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:36:57.920535Z", - "start_time": "2025-02-11T15:36:57.077187Z" + "end_time": "2025-03-30T16:44:16.291227Z", + "start_time": "2025-03-30T16:44:15.451043Z" } }, - "outputs": [], "source": [ "import tushare as ts\n", "import pandas as pd\n", "import time\n", + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", "\n", "ts.set_token('3a0741c702ee7e5e5f2bf1f0846bafaafe4e320833240b2a7e4a685f')\n", "pro = ts.pro_api()" - ] + ], + "outputs": [], + "execution_count": 1 }, { "cell_type": "code", - "execution_count": 2, "id": "d31855fab4f8b1bc", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:59:48.844910Z", - "start_time": "2025-02-11T15:59:40.161163Z" + "end_time": "2025-03-30T16:44:23.242175Z", + "start_time": "2025-03-30T16:44:16.454498Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Index: 8375079 entries, 0 to 5384\n", - "Data columns (total 2 columns):\n", - " # Column Dtype \n", - "--- ------ ----- \n", - " 0 ts_code object\n", - " 1 trade_date object\n", - "dtypes: object(2)\n", - "memory usage: 191.7+ MB\n", - "None\n", - "20250211\n", - "20250212\n" - ] - } - ], "source": [ "h5_filename = '../../../data/daily_data.h5'\n", "key = '/daily_data'\n", @@ -60,24 +41,90 @@ " max_date = df['trade_date'].max()\n", "\n", "print(max_date)\n", - "trade_cal = pro.trade_cal(exchange='', start_date='20170101', end_date='20250220')\n", + "trade_cal = pro.trade_cal(exchange='', start_date='20170101', end_date='20250420')\n", "trade_cal = trade_cal[trade_cal['is_open'] == 1] # 只保留交易日\n", "trade_dates = trade_cal[trade_cal['cal_date'] > max_date]['cal_date'].tolist()\n", "start_date = min(trade_dates)\n", "print(start_date)" - ] + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Index: 8525701 entries, 0 to 32262\n", + "Data columns (total 2 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object\n", + " 1 trade_date object\n", + "dtypes: object(2)\n", + "memory usage: 195.1+ MB\n", + "None\n", + "20250321\n", + "20250324\n" + ] + } + ], + "execution_count": 2 }, { "cell_type": "code", - "execution_count": 3, "id": "f403cc963e1d39b", "metadata": { + "scrolled": true, "ExecuteTime": { - "end_time": "2025-02-11T15:56:25.103931Z", - "start_time": "2025-02-11T15:37:54.637983Z" - }, - "scrolled": true + "end_time": "2025-03-30T16:56:34.154056Z", + "start_time": "2025-03-30T16:44:23.242175Z" + } }, + "source": [ + "from concurrent.futures import ThreadPoolExecutor, as_completed\n", + "\n", + "# 读取本地保存的股票列表 CSV 文件(假设文件名为 stocks_data.csv)\n", + "stocks_df = pd.read_csv('../../../stocks_list.csv', encoding='utf-8-sig')\n", + "\n", + "# 用于存放所有股票的日线数据(每次获取的 DataFrame)\n", + "daily_data_list = []\n", + "\n", + "# API 调用计数和时间控制变量\n", + "api_call_count = 0\n", + "batch_start_time = time.time()\n", + "\n", + "\n", + "def get_data(ts_code):\n", + " daily_data = ts.pro_bar(ts_code=ts_code, adj='hfq', start_date=start_date)\n", + " # 如果返回数据不为空,则添加一列标识股票代码\n", + " if daily_data is not None and not daily_data.empty:\n", + " return daily_data\n", + "\n", + "\n", + "# 循环遍历每个股票代码并获取数据\n", + "with ThreadPoolExecutor(max_workers=2) as executor:\n", + " future_to_date = {executor.submit(get_data, row['ts_code']): row['ts_code'] for idx, row in stocks_df.iterrows()}\n", + "\n", + " for future in as_completed(future_to_date):\n", + " ts_code = future_to_date[future]\n", + " try:\n", + " result = future.result()\n", + " daily_data_list.append(result)\n", + " print(f\"任务 {ts_code} 完成\")\n", + " except Exception as e:\n", + " print(f\"获取 {ts_code} 数据时出错: {e}\")\n", + " # 计数一次 API 调用\n", + " api_call_count += 1\n", + "\n", + " # 每调用300次,检查时间是否少于1分钟,如果少于则等待剩余时间\n", + " if api_call_count % 300 == 0:\n", + " elapsed = time.time() - batch_start_time\n", + " if elapsed < 60:\n", + " sleep_time = 60 - elapsed\n", + " print(f\"已调用300次API,等待 {sleep_time:.2f} 秒以满足速率限制...\")\n", + " time.sleep(sleep_time)\n", + " # 重置批次起始时间\n", + " batch_start_time = time.time()\n" + ], "outputs": [ { "name": "stdout", @@ -85,8 +132,8 @@ "text": [ "任务 000002.SZ 完成\n", "任务 000001.SZ 完成\n", - "任务 000006.SZ 完成\n", "任务 000004.SZ 完成\n", + "任务 000006.SZ 完成\n", "任务 000007.SZ 完成\n", "任务 000008.SZ 完成\n", "任务 000009.SZ 完成\n", @@ -134,12 +181,12 @@ "任务 000078.SZ 完成\n", "任务 000088.SZ 完成\n", "任务 000089.SZ 完成\n", - "任务 000090.SZ 完成\n", "任务 000096.SZ 完成\n", - "任务 000099.SZ 完成\n", + "任务 000090.SZ 完成\n", "任务 000100.SZ 完成\n", - "任务 000151.SZ 完成\n", + "任务 000099.SZ 完成\n", "任务 000153.SZ 完成\n", + "任务 000151.SZ 完成\n", "任务 000155.SZ 完成\n", "任务 000156.SZ 完成\n", "任务 000157.SZ 完成\n", @@ -161,11 +208,11 @@ "任务 000411.SZ 完成\n", "任务 000415.SZ 完成\n", "任务 000417.SZ 完成\n", - "任务 000420.SZ 完成\n", "任务 000419.SZ 完成\n", + "任务 000420.SZ 完成\n", "任务 000421.SZ 完成\n", - "任务 000422.SZ 完成\n", "任务 000423.SZ 完成\n", + "任务 000422.SZ 完成\n", "任务 000425.SZ 完成\n", "任务 000426.SZ 完成\n", "任务 000428.SZ 完成\n", @@ -185,28 +232,28 @@ "任务 000514.SZ 完成\n", "任务 000516.SZ 完成\n", "任务 000517.SZ 完成\n", - "任务 000519.SZ 完成\n", "任务 000518.SZ 完成\n", - "任务 000521.SZ 完成\n", + "任务 000519.SZ 完成\n", "任务 000520.SZ 完成\n", + "任务 000521.SZ 完成\n", "任务 000523.SZ 完成\n", "任务 000524.SZ 完成\n", "任务 000525.SZ 完成\n", "任务 000526.SZ 完成\n", - "任务 000529.SZ 完成\n", "任务 000528.SZ 完成\n", - "任务 000531.SZ 完成\n", + "任务 000529.SZ 完成\n", "任务 000530.SZ 完成\n", + "任务 000531.SZ 完成\n", "任务 000532.SZ 完成\n", "任务 000533.SZ 完成\n", - "任务 000536.SZ 完成\n", "任务 000534.SZ 完成\n", - "任务 000538.SZ 完成\n", + "任务 000536.SZ 完成\n", "任务 000537.SZ 完成\n", - "任务 000541.SZ 完成\n", + "任务 000538.SZ 完成\n", "任务 000539.SZ 完成\n", - "任务 000544.SZ 完成\n", + "任务 000541.SZ 完成\n", "任务 000543.SZ 完成\n", + "任务 000544.SZ 完成\n", "任务 000545.SZ 完成\n", "任务 000546.SZ 完成\n", "任务 000547.SZ 完成\n", @@ -214,12 +261,12 @@ "任务 000550.SZ 完成\n", "任务 000551.SZ 完成\n", "任务 000552.SZ 完成\n", - "任务 000553.SZ 完成\n", "任务 000554.SZ 完成\n", + "任务 000553.SZ 完成\n", "任务 000555.SZ 完成\n", "任务 000557.SZ 完成\n", - "任务 000558.SZ 完成\n", "任务 000559.SZ 完成\n", + "任务 000558.SZ 完成\n", "任务 000560.SZ 完成\n", "任务 000561.SZ 完成\n", "任务 000563.SZ 完成\n", @@ -237,22 +284,22 @@ "任务 000582.SZ 完成\n", "任务 000584.SZ 完成\n", "任务 000586.SZ 完成\n", - "任务 000590.SZ 完成\n", "任务 000589.SZ 完成\n", - "任务 000592.SZ 完成\n", + "任务 000590.SZ 完成\n", "任务 000591.SZ 完成\n", - "任务 000595.SZ 完成\n", + "任务 000592.SZ 完成\n", "任务 000593.SZ 完成\n", - "任务 000597.SZ 完成\n", + "任务 000595.SZ 完成\n", "任务 000596.SZ 完成\n", - "任务 000599.SZ 完成\n", + "任务 000597.SZ 完成\n", "任务 000598.SZ 完成\n", - "任务 000601.SZ 完成\n", + "任务 000599.SZ 完成\n", "任务 000600.SZ 完成\n", + "任务 000601.SZ 完成\n", "任务 000603.SZ 完成\n", "任务 000605.SZ 完成\n", - "任务 000608.SZ 完成\n", "任务 000607.SZ 完成\n", + "任务 000608.SZ 完成\n", "任务 000609.SZ 完成\n", "任务 000610.SZ 完成\n", "任务 000612.SZ 完成\n", @@ -260,13 +307,13 @@ "任务 000617.SZ 完成\n", "任务 000619.SZ 完成\n", "任务 000620.SZ 完成\n", + "任务 000623.SZ 完成\n", "任务 000622.SZ 完成\n", "任务 000625.SZ 完成\n", - "任务 000623.SZ 完成\n", "任务 000626.SZ 完成\n", "任务 000627.SZ 完成\n", - "任务 000629.SZ 完成\n", "任务 000628.SZ 完成\n", + "任务 000629.SZ 完成\n", "任务 000630.SZ 完成\n", "任务 000631.SZ 完成\n", "任务 000632.SZ 完成\n", @@ -295,16 +342,16 @@ "任务 000678.SZ 完成\n", "任务 000679.SZ 完成\n", "任务 000680.SZ 完成\n", - "任务 000682.SZ 完成\n", "任务 000681.SZ 完成\n", + "任务 000682.SZ 完成\n", "任务 000683.SZ 完成\n", "任务 000685.SZ 完成\n", "任务 000686.SZ 完成\n", "任务 000688.SZ 完成\n", "任务 000690.SZ 完成\n", "任务 000691.SZ 完成\n", - "任务 000695.SZ 完成\n", "任务 000692.SZ 完成\n", + "任务 000695.SZ 完成\n", "任务 000697.SZ 完成\n", "任务 000698.SZ 完成\n", "任务 000700.SZ 完成\n", @@ -323,14 +370,14 @@ "任务 000716.SZ 完成\n", "任务 000717.SZ 完成\n", "任务 000718.SZ 完成\n", - "任务 000719.SZ 完成\n", "任务 000720.SZ 完成\n", + "任务 000719.SZ 完成\n", "任务 000721.SZ 完成\n", "任务 000722.SZ 完成\n", - "任务 000723.SZ 完成\n", "任务 000725.SZ 完成\n", - "任务 000726.SZ 完成\n", + "任务 000723.SZ 完成\n", "任务 000727.SZ 完成\n", + "任务 000726.SZ 完成\n", "任务 000728.SZ 完成\n", "任务 000729.SZ 完成\n", "任务 000731.SZ 完成\n", @@ -357,8 +404,8 @@ "任务 000776.SZ 完成\n", "任务 000777.SZ 完成\n", "任务 000778.SZ 完成\n", - "任务 000779.SZ 完成\n", "任务 000782.SZ 完成\n", + "任务 000779.SZ 完成\n", "任务 000783.SZ 完成\n", "任务 000785.SZ 完成\n", "任务 000786.SZ 完成\n", @@ -367,10 +414,10 @@ "任务 000790.SZ 完成\n", "任务 000791.SZ 完成\n", "任务 000792.SZ 完成\n", - "任务 000793.SZ 完成\n", "任务 000795.SZ 完成\n", - "任务 000796.SZ 完成\n", + "任务 000793.SZ 完成\n", "任务 000797.SZ 完成\n", + "任务 000796.SZ 完成\n", "任务 000798.SZ 完成\n", "任务 000799.SZ 完成\n", "任务 000800.SZ 完成\n", @@ -401,8 +448,8 @@ "任务 000837.SZ 完成\n", "任务 000838.SZ 完成\n", "任务 000839.SZ 完成\n", - "任务 000848.SZ 完成\n", "任务 000850.SZ 完成\n", + "任务 000848.SZ 完成\n", "任务 000851.SZ 完成\n", "任务 000852.SZ 完成\n", "任务 000856.SZ 完成\n", @@ -417,12 +464,12 @@ "任务 000876.SZ 完成\n", "任务 000877.SZ 完成\n", "任务 000878.SZ 完成\n", - "任务 000880.SZ 完成\n", "任务 000881.SZ 完成\n", + "任务 000880.SZ 完成\n", "任务 000882.SZ 完成\n", "任务 000883.SZ 完成\n", - "任务 000886.SZ 完成\n", "任务 000885.SZ 完成\n", + "任务 000886.SZ 完成\n", "任务 000887.SZ 完成\n", "任务 000888.SZ 完成\n", "任务 000889.SZ 完成\n", @@ -486,16 +533,16 @@ "任务 000970.SZ 完成\n", "任务 000972.SZ 完成\n", "任务 000973.SZ 完成\n", - "任务 000977.SZ 完成\n", "任务 000975.SZ 完成\n", + "任务 000977.SZ 完成\n", "任务 000978.SZ 完成\n", "任务 000980.SZ 完成\n", "任务 000981.SZ 完成\n", "任务 000983.SZ 完成\n", "任务 000985.SZ 完成\n", "任务 000987.SZ 完成\n", - "任务 000989.SZ 完成\n", "任务 000988.SZ 完成\n", + "任务 000989.SZ 完成\n", "任务 000990.SZ 完成\n", "任务 000993.SZ 完成\n", "任务 000995.SZ 完成\n", @@ -523,36 +570,36 @@ "任务 001223.SZ 完成\n", "任务 001225.SZ 完成\n", "任务 001226.SZ 完成\n", - "任务 001227.SZ 完成\n", "任务 001228.SZ 完成\n", + "任务 001227.SZ 完成\n", "任务 001229.SZ 完成\n", "任务 001230.SZ 完成\n", - "任务 001231.SZ 完成\n", "任务 001234.SZ 完成\n", - "任务 001236.SZ 完成\n", + "任务 001231.SZ 完成\n", "任务 001238.SZ 完成\n", + "任务 001236.SZ 完成\n", "任务 001239.SZ 完成\n", "任务 001255.SZ 完成\n", - "任务 001256.SZ 完成\n", "任务 001258.SZ 完成\n", + "任务 001256.SZ 完成\n", "任务 001259.SZ 完成\n", "任务 001260.SZ 完成\n", "任务 001266.SZ 完成\n", "任务 001267.SZ 完成\n", "任务 001268.SZ 完成\n", "任务 001269.SZ 完成\n", - "任务 001270.SZ 完成\n", "任务 001277.SZ 完成\n", - "任务 001279.SZ 完成\n", + "任务 001270.SZ 完成\n", "任务 001278.SZ 完成\n", - "任务 001282.SZ 完成\n", + "任务 001279.SZ 完成\n", "任务 001283.SZ 完成\n", + "任务 001282.SZ 完成\n", "任务 001286.SZ 完成\n", "任务 001287.SZ 完成\n", - "任务 001289.SZ 完成\n", "任务 001288.SZ 完成\n", - "任务 001298.SZ 完成\n", + "任务 001289.SZ 完成\n", "任务 001296.SZ 完成\n", + "任务 001298.SZ 完成\n", "任务 001299.SZ 完成\n", "任务 001300.SZ 完成\n", "任务 001301.SZ 完成\n", @@ -731,8 +778,8 @@ "任务 002137.SZ 完成\n", "任务 002138.SZ 完成\n", "任务 002139.SZ 完成\n", - "任务 002140.SZ 完成\n", "任务 002141.SZ 完成\n", + "任务 002140.SZ 完成\n", "任务 002142.SZ 完成\n", "任务 002144.SZ 完成\n", "任务 002145.SZ 完成\n", @@ -799,12 +846,12 @@ "任务 002207.SZ 完成\n", "任务 002208.SZ 完成\n", "任务 002209.SZ 完成\n", - "任务 002210.SZ 完成\n", "任务 002211.SZ 完成\n", - "任务 002212.SZ 完成\n", + "任务 002210.SZ 完成\n", "任务 002213.SZ 完成\n", - "任务 002214.SZ 完成\n", + "任务 002212.SZ 完成\n", "任务 002215.SZ 完成\n", + "任务 002214.SZ 完成\n", "任务 002216.SZ 完成\n", "任务 002217.SZ 完成\n", "任务 002218.SZ 完成\n", @@ -826,32 +873,32 @@ "任务 002235.SZ 完成\n", "任务 002236.SZ 完成\n", "任务 002237.SZ 完成\n", - "任务 002239.SZ 完成\n", "任务 002238.SZ 完成\n", - "任务 002241.SZ 完成\n", + "任务 002239.SZ 完成\n", "任务 002240.SZ 完成\n", + "任务 002241.SZ 完成\n", "任务 002242.SZ 完成\n", "任务 002243.SZ 完成\n", "任务 002244.SZ 完成\n", "任务 002245.SZ 完成\n", - "任务 002247.SZ 完成\n", "任务 002246.SZ 完成\n", + "任务 002247.SZ 完成\n", "任务 002248.SZ 完成\n", "任务 002249.SZ 完成\n", + "任务 002250.SZ 完成\n", "任务 002251.SZ 完成\n", "任务 002252.SZ 完成\n", - "任务 002250.SZ 完成\n", "任务 002253.SZ 完成\n", "任务 002254.SZ 完成\n", "任务 002255.SZ 完成\n", - "任务 002258.SZ 完成\n", "任务 002256.SZ 完成\n", + "任务 002258.SZ 完成\n", "任务 002259.SZ 完成\n", "任务 002261.SZ 完成\n", - "任务 002263.SZ 完成\n", "任务 002262.SZ 完成\n", - "任务 002265.SZ 完成\n", + "任务 002263.SZ 完成\n", "任务 002264.SZ 完成\n", + "任务 002265.SZ 完成\n", "任务 002266.SZ 完成\n", "任务 002267.SZ 完成\n", "任务 002268.SZ 完成\n", @@ -868,8 +915,8 @@ "任务 002279.SZ 完成\n", "任务 002281.SZ 完成\n", "任务 002282.SZ 完成\n", - "任务 002284.SZ 完成\n", "任务 002283.SZ 完成\n", + "任务 002284.SZ 完成\n", "任务 002285.SZ 完成\n", "任务 002286.SZ 完成\n", "任务 002287.SZ 完成\n", @@ -956,10 +1003,10 @@ "任务 002373.SZ 完成\n", "任务 002374.SZ 完成\n", "任务 002375.SZ 完成\n", - "任务 002377.SZ 完成\n", "任务 002376.SZ 完成\n", - "任务 002379.SZ 完成\n", + "任务 002377.SZ 完成\n", "任务 002378.SZ 完成\n", + "任务 002379.SZ 完成\n", "任务 002380.SZ 完成\n", "任务 002381.SZ 完成\n", "任务 002382.SZ 完成\n", @@ -972,8 +1019,8 @@ "任务 002389.SZ 完成\n", "任务 002390.SZ 完成\n", "任务 002391.SZ 完成\n", - "任务 002393.SZ 完成\n", "任务 002392.SZ 完成\n", + "任务 002393.SZ 完成\n", "任务 002394.SZ 完成\n", "任务 002395.SZ 完成\n", "任务 002396.SZ 完成\n", @@ -990,16 +1037,16 @@ "任务 002407.SZ 完成\n", "任务 002408.SZ 完成\n", "任务 002409.SZ 完成\n", - "任务 002410.SZ 完成\n", "任务 002412.SZ 完成\n", - "任务 002413.SZ 完成\n", + "任务 002410.SZ 完成\n", "任务 002414.SZ 完成\n", - "任务 002415.SZ 完成\n", + "任务 002413.SZ 完成\n", "任务 002416.SZ 完成\n", + "任务 002415.SZ 完成\n", "任务 002418.SZ 完成\n", "任务 002419.SZ 完成\n", - "任务 002420.SZ 完成\n", "任务 002421.SZ 完成\n", + "任务 002420.SZ 完成\n", "任务 002422.SZ 完成\n", "任务 002423.SZ 完成\n", "任务 002424.SZ 完成\n", @@ -1008,14 +1055,14 @@ "任务 002427.SZ 完成\n", "任务 002428.SZ 完成\n", "任务 002429.SZ 完成\n", - "任务 002431.SZ 完成\n", "任务 002430.SZ 完成\n", + "任务 002431.SZ 完成\n", "任务 002432.SZ 完成\n", "任务 002434.SZ 完成\n", - "任务 002437.SZ 完成\n", "任务 002436.SZ 完成\n", - "任务 002439.SZ 完成\n", + "任务 002437.SZ 完成\n", "任务 002438.SZ 完成\n", + "任务 002439.SZ 完成\n", "任务 002440.SZ 完成\n", "任务 002441.SZ 完成\n", "任务 002442.SZ 完成\n", @@ -1026,30 +1073,30 @@ "任务 002448.SZ 完成\n", "任务 002449.SZ 完成\n", "任务 002451.SZ 完成\n", - "任务 002452.SZ 完成\n", "任务 002453.SZ 完成\n", - "任务 002454.SZ 完成\n", + "任务 002452.SZ 完成\n", "任务 002455.SZ 完成\n", + "任务 002454.SZ 完成\n", "任务 002456.SZ 完成\n", "任务 002457.SZ 完成\n", "任务 002458.SZ 完成\n", "任务 002459.SZ 完成\n", - "任务 002460.SZ 完成\n", "任务 002461.SZ 完成\n", - "任务 002462.SZ 完成\n", + "任务 002460.SZ 完成\n", "任务 002463.SZ 完成\n", - "任务 002466.SZ 完成\n", + "任务 002462.SZ 完成\n", "任务 002465.SZ 完成\n", - "任务 002467.SZ 完成\n", + "任务 002466.SZ 完成\n", "任务 002468.SZ 完成\n", - "任务 002469.SZ 完成\n", + "任务 002467.SZ 完成\n", "任务 002470.SZ 完成\n", - "任务 002472.SZ 完成\n", + "任务 002469.SZ 完成\n", "任务 002471.SZ 完成\n", - "任务 002474.SZ 完成\n", + "任务 002472.SZ 完成\n", "任务 002475.SZ 完成\n", - "任务 002476.SZ 完成\n", + "任务 002474.SZ 完成\n", "任务 002478.SZ 完成\n", + "任务 002476.SZ 完成\n", "任务 002479.SZ 完成\n", "任务 002480.SZ 完成\n", "任务 002481.SZ 完成\n", @@ -1062,8 +1109,8 @@ "任务 002488.SZ 完成\n", "任务 002489.SZ 完成\n", "任务 002490.SZ 完成\n", - "任务 002491.SZ 完成\n", "任务 002492.SZ 完成\n", + "任务 002491.SZ 完成\n", "任务 002493.SZ 完成\n", "任务 002494.SZ 完成\n", "任务 002495.SZ 完成\n", @@ -1076,24 +1123,24 @@ "任务 002507.SZ 完成\n", "任务 002508.SZ 完成\n", "任务 002510.SZ 完成\n", - "任务 002511.SZ 完成\n", "任务 002512.SZ 完成\n", + "任务 002511.SZ 完成\n", "任务 002513.SZ 完成\n", "任务 002514.SZ 完成\n", "任务 002515.SZ 完成\n", "任务 002516.SZ 完成\n", "任务 002517.SZ 完成\n", "任务 002518.SZ 完成\n", - "任务 002519.SZ 完成\n", "任务 002520.SZ 完成\n", - "任务 002521.SZ 完成\n", + "任务 002519.SZ 完成\n", "任务 002522.SZ 完成\n", - "任务 002523.SZ 完成\n", + "任务 002521.SZ 完成\n", "任务 002524.SZ 完成\n", + "任务 002523.SZ 完成\n", "任务 002526.SZ 完成\n", "任务 002527.SZ 完成\n", - "任务 002528.SZ 完成\n", "任务 002529.SZ 完成\n", + "任务 002528.SZ 完成\n", "任务 002530.SZ 完成\n", "任务 002531.SZ 完成\n", "任务 002532.SZ 完成\n", @@ -1102,8 +1149,8 @@ "任务 002535.SZ 完成\n", "任务 002536.SZ 完成\n", "任务 002537.SZ 完成\n", - "任务 002538.SZ 完成\n", "任务 002539.SZ 完成\n", + "任务 002538.SZ 完成\n", "任务 002540.SZ 完成\n", "任务 002541.SZ 完成\n", "任务 002542.SZ 完成\n", @@ -1118,8 +1165,8 @@ "任务 002551.SZ 完成\n", "任务 002552.SZ 完成\n", "任务 002553.SZ 完成\n", - "任务 002554.SZ 完成\n", "任务 002555.SZ 完成\n", + "任务 002554.SZ 完成\n", "任务 002556.SZ 完成\n", "任务 002557.SZ 完成\n", "任务 002558.SZ 完成\n", @@ -1155,12 +1202,12 @@ "任务 002588.SZ 完成\n", "任务 002589.SZ 完成\n", "任务 002590.SZ 完成\n", - "任务 002591.SZ 完成\n", "任务 002592.SZ 完成\n", + "任务 002591.SZ 完成\n", "任务 002593.SZ 完成\n", "任务 002594.SZ 完成\n", - "任务 002595.SZ 完成\n", "任务 002596.SZ 完成\n", + "任务 002595.SZ 完成\n", "任务 002597.SZ 完成\n", "任务 002598.SZ 完成\n", "任务 002599.SZ 完成\n", @@ -1181,10 +1228,10 @@ "任务 002616.SZ 完成\n", "任务 002617.SZ 完成\n", "任务 002620.SZ 完成\n", - "任务 002622.SZ 完成\n", "任务 002623.SZ 完成\n", - "任务 002624.SZ 完成\n", + "任务 002622.SZ 完成\n", "任务 002625.SZ 完成\n", + "任务 002624.SZ 完成\n", "任务 002626.SZ 完成\n", "任务 002627.SZ 完成\n", "任务 002628.SZ 完成\n", @@ -1219,8 +1266,8 @@ "任务 002657.SZ 完成\n", "任务 002658.SZ 完成\n", "任务 002659.SZ 完成\n", - "任务 002660.SZ 完成\n", "任务 002661.SZ 完成\n", + "任务 002660.SZ 完成\n", "任务 002662.SZ 完成\n", "任务 002663.SZ 完成\n", "任务 002664.SZ 完成\n", @@ -1311,8 +1358,8 @@ "任务 002761.SZ 完成\n", "任务 002762.SZ 完成\n", "任务 002763.SZ 完成\n", - "任务 002766.SZ 完成\n", "任务 002765.SZ 完成\n", + "任务 002766.SZ 完成\n", "任务 002767.SZ 完成\n", "任务 002768.SZ 完成\n", "任务 002769.SZ 完成\n", @@ -1330,9 +1377,9 @@ "任务 002785.SZ 完成\n", "任务 002786.SZ 完成\n", "任务 002787.SZ 完成\n", + "任务 002788.SZ 完成\n", "任务 002789.SZ 完成\n", "任务 002790.SZ 完成\n", - "任务 002788.SZ 完成\n", "任务 002791.SZ 完成\n", "任务 002792.SZ 完成\n", "任务 002793.SZ 完成\n", @@ -1390,16 +1437,16 @@ "任务 002850.SZ 完成\n", "任务 002851.SZ 完成\n", "任务 002852.SZ 完成\n", - "任务 002853.SZ 完成\n", "任务 002855.SZ 完成\n", - "任务 002856.SZ 完成\n", + "任务 002853.SZ 完成\n", "任务 002857.SZ 完成\n", - "任务 002858.SZ 完成\n", + "任务 002856.SZ 完成\n", "任务 002859.SZ 完成\n", - "任务 002860.SZ 完成\n", + "任务 002858.SZ 完成\n", "任务 002861.SZ 完成\n", - "任务 002862.SZ 完成\n", + "任务 002860.SZ 完成\n", "任务 002863.SZ 完成\n", + "任务 002862.SZ 完成\n", "任务 002864.SZ 完成\n", "任务 002865.SZ 完成\n", "任务 002866.SZ 完成\n", @@ -1440,17 +1487,17 @@ "任务 002903.SZ 完成\n", "任务 002905.SZ 完成\n", "任务 002906.SZ 完成\n", - "任务 002908.SZ 完成\n", "任务 002907.SZ 完成\n", "任务 002909.SZ 完成\n", + "任务 002908.SZ 完成\n", "任务 002910.SZ 完成\n", "任务 002911.SZ 完成\n", "任务 002912.SZ 完成\n", "任务 002913.SZ 完成\n", - "任务 002915.SZ 完成\n", "任务 002916.SZ 完成\n", - "任务 002917.SZ 完成\n", + "任务 002915.SZ 完成\n", "任务 002918.SZ 完成\n", + "任务 002917.SZ 完成\n", "任务 002919.SZ 完成\n", "任务 002920.SZ 完成\n", "任务 002921.SZ 完成\n", @@ -1492,30 +1539,30 @@ "任务 002961.SZ 完成\n", "任务 002962.SZ 完成\n", "任务 002963.SZ 完成\n", - "任务 002966.SZ 完成\n", "任务 002965.SZ 完成\n", - "任务 002968.SZ 完成\n", + "任务 002966.SZ 完成\n", "任务 002967.SZ 完成\n", - "任务 002970.SZ 完成\n", + "任务 002968.SZ 完成\n", "任务 002969.SZ 完成\n", + "任务 002970.SZ 完成\n", "任务 002971.SZ 完成\n", "任务 002972.SZ 完成\n", - "任务 002975.SZ 完成\n", "任务 002973.SZ 完成\n", + "任务 002975.SZ 完成\n", "任务 002976.SZ 完成\n", "任务 002977.SZ 完成\n", - "任务 002979.SZ 完成\n", "任务 002978.SZ 完成\n", + "任务 002979.SZ 完成\n", "任务 002980.SZ 完成\n", "任务 002981.SZ 完成\n", "任务 002982.SZ 完成\n", "任务 002983.SZ 完成\n", - "任务 002985.SZ 完成\n", "任务 002984.SZ 完成\n", + "任务 002985.SZ 完成\n", "任务 002986.SZ 完成\n", "任务 002987.SZ 完成\n", - "任务 002989.SZ 完成\n", "任务 002988.SZ 完成\n", + "任务 002989.SZ 完成\n", "任务 002990.SZ 完成\n", "任务 002991.SZ 完成\n", "任务 002992.SZ 完成\n", @@ -1555,13 +1602,13 @@ "任务 003029.SZ 完成\n", "任务 003030.SZ 完成\n", "任务 003031.SZ 完成\n", - "任务 003032.SZ 完成\n", "任务 003033.SZ 完成\n", + "任务 003032.SZ 完成\n", + "任务 003036.SZ 完成\n", "任务 003035.SZ 完成\n", "任务 003037.SZ 完成\n", - "任务 003036.SZ 完成\n", - "任务 003039.SZ 完成\n", "任务 003038.SZ 完成\n", + "任务 003039.SZ 完成\n", "任务 003040.SZ 完成\n", "任务 003041.SZ 完成\n", "任务 003042.SZ 完成\n", @@ -1584,36 +1631,36 @@ "任务 300015.SZ 完成\n", "任务 300016.SZ 完成\n", "任务 300017.SZ 完成\n", - "任务 300019.SZ 完成\n", "任务 300018.SZ 完成\n", + "任务 300019.SZ 完成\n", "任务 300020.SZ 完成\n", "任务 300021.SZ 完成\n", "任务 300022.SZ 完成\n", "任务 300024.SZ 完成\n", - "任务 300026.SZ 完成\n", "任务 300025.SZ 完成\n", + "任务 300026.SZ 完成\n", "任务 300027.SZ 完成\n", "任务 300029.SZ 完成\n", - "任务 300031.SZ 完成\n", "任务 300030.SZ 完成\n", - "任务 300033.SZ 完成\n", + "任务 300031.SZ 完成\n", "任务 300032.SZ 完成\n", + "任务 300033.SZ 完成\n", "任务 300034.SZ 完成\n", "任务 300035.SZ 完成\n", "任务 300036.SZ 完成\n", "任务 300037.SZ 完成\n", - "任务 300040.SZ 完成\n", "任务 300039.SZ 完成\n", - "任务 300042.SZ 完成\n", + "任务 300040.SZ 完成\n", "任务 300041.SZ 完成\n", + "任务 300042.SZ 完成\n", "任务 300043.SZ 完成\n", "任务 300044.SZ 完成\n", "任务 300045.SZ 完成\n", "任务 300046.SZ 完成\n", "任务 300047.SZ 完成\n", "任务 300048.SZ 完成\n", - "任务 300050.SZ 完成\n", "任务 300049.SZ 完成\n", + "任务 300050.SZ 完成\n", "任务 300051.SZ 完成\n", "任务 300052.SZ 完成\n", "任务 300053.SZ 完成\n", @@ -1624,20 +1671,20 @@ "任务 300058.SZ 完成\n", "任务 300059.SZ 完成\n", "任务 300061.SZ 完成\n", - "任务 300063.SZ 完成\n", "任务 300062.SZ 完成\n", - "任务 300066.SZ 完成\n", + "任务 300063.SZ 完成\n", "任务 300065.SZ 完成\n", - "任务 300068.SZ 完成\n", + "任务 300066.SZ 完成\n", "任务 300067.SZ 完成\n", - "任务 300070.SZ 完成\n", + "任务 300068.SZ 完成\n", "任务 300069.SZ 完成\n", + "任务 300070.SZ 完成\n", "任务 300071.SZ 完成\n", "任务 300072.SZ 完成\n", "任务 300073.SZ 完成\n", "任务 300074.SZ 完成\n", - "任务 300076.SZ 完成\n", "任务 300075.SZ 完成\n", + "任务 300076.SZ 完成\n", "任务 300077.SZ 完成\n", "任务 300078.SZ 完成\n", "任务 300079.SZ 完成\n", @@ -1685,8 +1732,8 @@ "任务 300125.SZ 完成\n", "任务 300126.SZ 完成\n", "任务 300127.SZ 完成\n", - "任务 300129.SZ 完成\n", "任务 300128.SZ 完成\n", + "任务 300129.SZ 完成\n", "任务 300130.SZ 完成\n", "任务 300131.SZ 完成\n", "任务 300132.SZ 完成\n", @@ -1695,40 +1742,40 @@ "任务 300135.SZ 完成\n", "任务 300136.SZ 完成\n", "任务 300137.SZ 完成\n", - "任务 300139.SZ 完成\n", "任务 300138.SZ 完成\n", + "任务 300139.SZ 完成\n", "任务 300140.SZ 完成\n", "任务 300141.SZ 完成\n", "任务 300142.SZ 完成\n", "任务 300143.SZ 完成\n", "任务 300145.SZ 完成\n", "任务 300144.SZ 完成\n", - "任务 300146.SZ 完成\n", "任务 300147.SZ 完成\n", + "任务 300146.SZ 完成\n", "任务 300148.SZ 完成\n", "任务 300149.SZ 完成\n", - "任务 300150.SZ 完成\n", "任务 300151.SZ 完成\n", + "任务 300150.SZ 完成\n", "任务 300152.SZ 完成\n", "任务 300153.SZ 完成\n", "任务 300154.SZ 完成\n", "任务 300155.SZ 完成\n", "任务 300157.SZ 完成\n", "任务 300158.SZ 完成\n", - "任务 300159.SZ 完成\n", "任务 300160.SZ 完成\n", - "任务 300161.SZ 完成\n", + "任务 300159.SZ 完成\n", "任务 300162.SZ 完成\n", + "任务 300161.SZ 完成\n", "任务 300163.SZ 完成\n", "任务 300164.SZ 完成\n", "任务 300165.SZ 完成\n", "任务 300166.SZ 完成\n", - "任务 300167.SZ 完成\n", "任务 300168.SZ 完成\n", + "任务 300167.SZ 完成\n", "任务 300169.SZ 完成\n", "任务 300170.SZ 完成\n", - "任务 300171.SZ 完成\n", "任务 300172.SZ 完成\n", + "任务 300171.SZ 完成\n", "任务 300173.SZ 完成\n", "任务 300174.SZ 完成\n", "任务 300175.SZ 完成\n", @@ -1763,26 +1810,26 @@ "任务 300207.SZ 完成\n", "任务 300208.SZ 完成\n", "任务 300209.SZ 完成\n", - "任务 300211.SZ 完成\n", "任务 300210.SZ 完成\n", + "任务 300211.SZ 完成\n", "任务 300212.SZ 完成\n", "任务 300213.SZ 完成\n", "任务 300214.SZ 完成\n", "任务 300215.SZ 完成\n", "任务 300217.SZ 完成\n", "任务 300218.SZ 完成\n", - "任务 300220.SZ 完成\n", "任务 300219.SZ 完成\n", - "任务 300222.SZ 完成\n", + "任务 300220.SZ 完成\n", "任务 300221.SZ 完成\n", - "任务 300224.SZ 完成\n", + "任务 300222.SZ 完成\n", "任务 300223.SZ 完成\n", - "任务 300226.SZ 完成\n", + "任务 300224.SZ 完成\n", "任务 300225.SZ 完成\n", + "任务 300226.SZ 完成\n", "任务 300227.SZ 完成\n", "任务 300228.SZ 完成\n", - "任务 300230.SZ 完成\n", "任务 300229.SZ 完成\n", + "任务 300230.SZ 完成\n", "任务 300231.SZ 完成\n", "任务 300232.SZ 完成\n", "任务 300233.SZ 完成\n", @@ -1800,9 +1847,9 @@ "任务 300245.SZ 完成\n", "任务 300246.SZ 完成\n", "任务 300247.SZ 完成\n", + "任务 300248.SZ 完成\n", "任务 300249.SZ 完成\n", "任务 300250.SZ 完成\n", - "任务 300248.SZ 完成\n", "任务 300251.SZ 完成\n", "任务 300252.SZ 完成\n", "任务 300253.SZ 完成\n", @@ -1851,21 +1898,21 @@ "任务 300300.SZ 完成\n", "任务 300301.SZ 完成\n", "任务 300302.SZ 完成\n", - "任务 300304.SZ 完成\n", "任务 300303.SZ 完成\n", + "任务 300304.SZ 完成\n", "任务 300305.SZ 完成\n", "任务 300306.SZ 完成\n", - "任务 300308.SZ 完成\n", "任务 300307.SZ 完成\n", - "任务 300311.SZ 完成\n", + "任务 300308.SZ 完成\n", "任务 300310.SZ 完成\n", - "任务 300314.SZ 完成\n", + "任务 300311.SZ 完成\n", "任务 300313.SZ 完成\n", - "任务 300316.SZ 完成\n", + "任务 300314.SZ 完成\n", "任务 300315.SZ 完成\n", + "任务 300316.SZ 完成\n", + "任务 300317.SZ 完成\n", "任务 300318.SZ 完成\n", "任务 300319.SZ 完成\n", - "任务 300317.SZ 完成\n", "任务 300320.SZ 完成\n", "任务 300321.SZ 完成\n", "任务 300322.SZ 完成\n", @@ -1920,14 +1967,14 @@ "任务 300379.SZ 完成\n", "任务 300380.SZ 完成\n", "任务 300381.SZ 完成\n", - "任务 300383.SZ 完成\n", "任务 300382.SZ 完成\n", - "任务 300385.SZ 完成\n", + "任务 300383.SZ 完成\n", "任务 300384.SZ 完成\n", + "任务 300385.SZ 完成\n", "任务 300386.SZ 完成\n", "任务 300387.SZ 完成\n", - "任务 300389.SZ 完成\n", "任务 300388.SZ 完成\n", + "任务 300389.SZ 完成\n", "任务 300390.SZ 完成\n", "任务 300391.SZ 完成\n", "任务 300393.SZ 完成\n", @@ -1939,8 +1986,8 @@ "任务 300399.SZ 完成\n", "任务 300400.SZ 完成\n", "任务 300401.SZ 完成\n", - "任务 300402.SZ 完成\n", "任务 300403.SZ 完成\n", + "任务 300402.SZ 完成\n", "任务 300404.SZ 完成\n", "任务 300405.SZ 完成\n", "任务 300406.SZ 完成\n", @@ -1948,34 +1995,34 @@ "任务 300408.SZ 完成\n", "任务 300409.SZ 完成\n", "任务 300410.SZ 完成\n", - "任务 300412.SZ 完成\n", "任务 300411.SZ 完成\n", + "任务 300412.SZ 完成\n", "任务 300413.SZ 完成\n", - "任务 300414.SZ 完成\n", "任务 300415.SZ 完成\n", + "任务 300414.SZ 完成\n", "任务 300416.SZ 完成\n", "任务 300417.SZ 完成\n", "任务 300418.SZ 完成\n", - "任务 300420.SZ 完成\n", "任务 300419.SZ 完成\n", - "任务 300422.SZ 完成\n", + "任务 300420.SZ 完成\n", "任务 300421.SZ 完成\n", - "任务 300424.SZ 完成\n", + "任务 300422.SZ 完成\n", "任务 300423.SZ 完成\n", + "任务 300424.SZ 完成\n", "任务 300425.SZ 完成\n", "任务 300426.SZ 完成\n", "任务 300427.SZ 完成\n", "任务 300428.SZ 完成\n", - "任务 300430.SZ 完成\n", "任务 300429.SZ 完成\n", + "任务 300430.SZ 完成\n", "任务 300432.SZ 完成\n", "任务 300433.SZ 完成\n", - "任务 300435.SZ 完成\n", "任务 300434.SZ 完成\n", + "任务 300435.SZ 完成\n", "任务 300436.SZ 完成\n", "任务 300437.SZ 完成\n", - "任务 300439.SZ 完成\n", "任务 300438.SZ 完成\n", + "任务 300439.SZ 完成\n", "任务 300440.SZ 完成\n", "任务 300441.SZ 完成\n", "任务 300442.SZ 完成\n", @@ -2010,12 +2057,12 @@ "任务 300471.SZ 完成\n", "任务 300472.SZ 完成\n", "任务 300473.SZ 完成\n", - "任务 300474.SZ 完成\n", "任务 300475.SZ 完成\n", + "任务 300474.SZ 完成\n", "任务 300476.SZ 完成\n", "任务 300477.SZ 完成\n", - "任务 300478.SZ 完成\n", "任务 300479.SZ 完成\n", + "任务 300478.SZ 完成\n", "任务 300480.SZ 完成\n", "任务 300481.SZ 完成\n", "任务 300482.SZ 完成\n", @@ -2038,14 +2085,14 @@ "任务 300500.SZ 完成\n", "任务 300501.SZ 完成\n", "任务 300502.SZ 完成\n", - "任务 300504.SZ 完成\n", "任务 300503.SZ 完成\n", + "任务 300504.SZ 完成\n", "任务 300505.SZ 完成\n", "任务 300506.SZ 完成\n", "任务 300507.SZ 完成\n", "任务 300508.SZ 完成\n", - "任务 300510.SZ 完成\n", "任务 300509.SZ 完成\n", + "任务 300510.SZ 完成\n", "任务 300511.SZ 完成\n", "任务 300512.SZ 完成\n", "任务 300513.SZ 完成\n", @@ -2080,12 +2127,12 @@ "任务 300545.SZ 完成\n", "任务 300546.SZ 完成\n", "任务 300547.SZ 完成\n", - "任务 300549.SZ 完成\n", "任务 300548.SZ 完成\n", - "任务 300551.SZ 完成\n", + "任务 300549.SZ 完成\n", "任务 300550.SZ 完成\n", - "任务 300553.SZ 完成\n", + "任务 300551.SZ 完成\n", "任务 300552.SZ 完成\n", + "任务 300553.SZ 完成\n", "任务 300554.SZ 完成\n", "任务 300555.SZ 完成\n", "任务 300556.SZ 完成\n", @@ -2094,8 +2141,8 @@ "任务 300559.SZ 完成\n", "任务 300560.SZ 完成\n", "任务 300561.SZ 完成\n", - "任务 300563.SZ 完成\n", "任务 300562.SZ 完成\n", + "任务 300563.SZ 完成\n", "任务 300564.SZ 完成\n", "任务 300565.SZ 完成\n", "任务 300566.SZ 完成\n", @@ -2109,8 +2156,8 @@ "任务 300575.SZ 完成\n", "任务 300576.SZ 完成\n", "任务 300577.SZ 完成\n", - "任务 300579.SZ 完成\n", "任务 300578.SZ 完成\n", + "任务 300579.SZ 完成\n", "任务 300580.SZ 完成\n", "任务 300581.SZ 完成\n", "任务 300582.SZ 完成\n", @@ -2133,39 +2180,39 @@ "任务 300599.SZ 完成\n", "任务 300600.SZ 完成\n", "任务 300601.SZ 完成\n", - "任务 300603.SZ 完成\n", "任务 300602.SZ 完成\n", + "任务 300603.SZ 完成\n", "任务 300604.SZ 完成\n", - "任务 300606.SZ 完成\n", "任务 300605.SZ 完成\n", + "任务 300606.SZ 完成\n", "任务 300607.SZ 完成\n", "任务 300608.SZ 完成\n", "任务 300609.SZ 完成\n", "任务 300610.SZ 完成\n", "任务 300611.SZ 完成\n", "任务 300612.SZ 完成\n", - "任务 300614.SZ 完成\n", "任务 300613.SZ 完成\n", + "任务 300614.SZ 完成\n", "任务 300615.SZ 完成\n", "任务 300616.SZ 完成\n", - "任务 300618.SZ 完成\n", "任务 300617.SZ 完成\n", + "任务 300618.SZ 完成\n", "任务 300619.SZ 完成\n", "任务 300620.SZ 完成\n", "任务 300621.SZ 完成\n", "任务 300622.SZ 完成\n", "任务 300623.SZ 完成\n", "任务 300624.SZ 完成\n", - "任务 300626.SZ 完成\n", "任务 300625.SZ 完成\n", + "任务 300626.SZ 完成\n", "任务 300627.SZ 完成\n", "任务 300628.SZ 完成\n", "任务 300629.SZ 完成\n", "任务 300630.SZ 完成\n", "任务 300631.SZ 完成\n", "任务 300632.SZ 完成\n", - "任务 300634.SZ 完成\n", "任务 300633.SZ 完成\n", + "任务 300634.SZ 完成\n", "任务 300635.SZ 完成\n", "任务 300636.SZ 完成\n", "任务 300637.SZ 完成\n", @@ -2198,8 +2245,8 @@ "任务 300665.SZ 完成\n", "任务 300666.SZ 完成\n", "任务 300667.SZ 完成\n", - "任务 300668.SZ 完成\n", "任务 300669.SZ 完成\n", + "任务 300668.SZ 完成\n", "任务 300670.SZ 完成\n", "任务 300671.SZ 完成\n", "任务 300672.SZ 完成\n", @@ -2231,20 +2278,20 @@ "任务 300698.SZ 完成\n", "任务 300699.SZ 完成\n", "任务 300700.SZ 完成\n", - "任务 300702.SZ 完成\n", "任务 300701.SZ 完成\n", - "任务 300705.SZ 完成\n", + "任务 300702.SZ 完成\n", "任务 300703.SZ 完成\n", - "任务 300707.SZ 完成\n", + "任务 300705.SZ 完成\n", "任务 300706.SZ 完成\n", - "任务 300709.SZ 完成\n", + "任务 300707.SZ 完成\n", "任务 300708.SZ 完成\n", - "任务 300711.SZ 完成\n", + "任务 300709.SZ 完成\n", "任务 300710.SZ 完成\n", + "任务 300711.SZ 完成\n", "任务 300712.SZ 完成\n", "任务 300713.SZ 完成\n", - "任务 300716.SZ 完成\n", "任务 300715.SZ 完成\n", + "任务 300716.SZ 完成\n", "任务 300717.SZ 完成\n", "任务 300718.SZ 完成\n", "任务 300719.SZ 完成\n", @@ -2270,10 +2317,10 @@ "任务 300741.SZ 完成\n", "任务 300743.SZ 完成\n", "任务 300745.SZ 完成\n", - "任务 300747.SZ 完成\n", "任务 300746.SZ 完成\n", - "任务 300749.SZ 完成\n", + "任务 300747.SZ 完成\n", "任务 300748.SZ 完成\n", + "任务 300749.SZ 完成\n", "任务 300750.SZ 完成\n", "任务 300751.SZ 完成\n", "任务 300752.SZ 完成\n", @@ -2296,8 +2343,8 @@ "任务 300771.SZ 完成\n", "任务 300772.SZ 完成\n", "任务 300773.SZ 完成\n", - "任务 300775.SZ 完成\n", "任务 300774.SZ 完成\n", + "任务 300775.SZ 完成\n", "任务 300776.SZ 完成\n", "任务 300777.SZ 完成\n", "任务 300778.SZ 完成\n", @@ -2324,24 +2371,24 @@ "任务 300801.SZ 完成\n", "任务 300802.SZ 完成\n", "任务 300803.SZ 完成\n", + "任务 300805.SZ 完成\n", "任务 300804.SZ 完成\n", "任务 300806.SZ 完成\n", - "任务 300805.SZ 完成\n", - "任务 300808.SZ 完成\n", "任务 300807.SZ 完成\n", + "任务 300808.SZ 完成\n", "任务 300809.SZ 完成\n", - "任务 300810.SZ 完成\n", "任务 300811.SZ 完成\n", + "任务 300810.SZ 完成\n", "任务 300812.SZ 完成\n", "任务 300813.SZ 完成\n", - "任务 300815.SZ 完成\n", "任务 300814.SZ 完成\n", + "任务 300815.SZ 完成\n", "任务 300816.SZ 完成\n", "任务 300817.SZ 完成\n", - "任务 300818.SZ 完成\n", "任务 300819.SZ 完成\n", - "任务 300820.SZ 完成\n", + "任务 300818.SZ 完成\n", "任务 300821.SZ 完成\n", + "任务 300820.SZ 完成\n", "任务 300822.SZ 完成\n", "任务 300823.SZ 完成\n", "任务 300824.SZ 完成\n", @@ -2378,8 +2425,8 @@ "任务 300855.SZ 完成\n", "任务 300856.SZ 完成\n", "任务 300857.SZ 完成\n", - "任务 300858.SZ 完成\n", "任务 300859.SZ 完成\n", + "任务 300858.SZ 完成\n", "任务 300860.SZ 完成\n", "任务 300861.SZ 完成\n", "任务 300862.SZ 完成\n", @@ -2388,8 +2435,8 @@ "任务 300865.SZ 完成\n", "任务 300866.SZ 完成\n", "任务 300867.SZ 完成\n", - "任务 300868.SZ 完成\n", "任务 300869.SZ 完成\n", + "任务 300868.SZ 完成\n", "任务 300870.SZ 完成\n", "任务 300871.SZ 完成\n", "任务 300872.SZ 完成\n", @@ -2398,8 +2445,8 @@ "任务 300876.SZ 完成\n", "任务 300877.SZ 完成\n", "任务 300878.SZ 完成\n", - "任务 300879.SZ 完成\n", "任务 300880.SZ 完成\n", + "任务 300879.SZ 完成\n", "任务 300881.SZ 完成\n", "任务 300882.SZ 完成\n", "任务 300883.SZ 完成\n", @@ -2418,26 +2465,26 @@ "任务 300896.SZ 完成\n", "任务 300897.SZ 完成\n", "任务 300898.SZ 完成\n", - "任务 300899.SZ 完成\n", "任务 300900.SZ 完成\n", - "任务 300901.SZ 完成\n", + "任务 300899.SZ 完成\n", "任务 300902.SZ 完成\n", - "任务 300903.SZ 完成\n", + "任务 300901.SZ 完成\n", "任务 300904.SZ 完成\n", - "任务 300906.SZ 完成\n", + "任务 300903.SZ 完成\n", "任务 300905.SZ 完成\n", + "任务 300906.SZ 完成\n", "任务 300908.SZ 完成\n", "任务 300907.SZ 完成\n", - "任务 300910.SZ 完成\n", "任务 300909.SZ 完成\n", - "任务 300912.SZ 完成\n", + "任务 300910.SZ 完成\n", "任务 300911.SZ 完成\n", - "任务 300915.SZ 完成\n", + "任务 300912.SZ 完成\n", "任务 300913.SZ 完成\n", + "任务 300915.SZ 完成\n", "任务 300916.SZ 完成\n", "任务 300917.SZ 完成\n", - "任务 300919.SZ 完成\n", "任务 300918.SZ 完成\n", + "任务 300919.SZ 完成\n", "任务 300920.SZ 完成\n", "任务 300921.SZ 完成\n", "任务 300923.SZ 完成\n", @@ -2448,22 +2495,22 @@ "任务 300928.SZ 完成\n", "任务 300929.SZ 完成\n", "任务 300930.SZ 完成\n", - "任务 300932.SZ 完成\n", "任务 300931.SZ 完成\n", + "任务 300932.SZ 完成\n", "任务 300933.SZ 完成\n", "任务 300935.SZ 完成\n", - "任务 300937.SZ 完成\n", "任务 300936.SZ 完成\n", + "任务 300937.SZ 完成\n", "任务 300938.SZ 完成\n", "任务 300939.SZ 完成\n", "任务 300940.SZ 完成\n", "任务 300941.SZ 完成\n", "任务 300942.SZ 完成\n", "任务 300943.SZ 完成\n", - "任务 300946.SZ 完成\n", "任务 300945.SZ 完成\n", - "任务 300948.SZ 完成\n", + "任务 300946.SZ 完成\n", "任务 300947.SZ 完成\n", + "任务 300948.SZ 完成\n", "任务 300949.SZ 完成\n", "任务 300950.SZ 完成\n", "任务 300951.SZ 完成\n", @@ -2485,28 +2532,28 @@ "任务 300968.SZ 完成\n", "任务 300969.SZ 完成\n", "任务 300970.SZ 完成\n", - "任务 300972.SZ 完成\n", "任务 300971.SZ 完成\n", - "任务 300975.SZ 完成\n", + "任务 300972.SZ 完成\n", "任务 300973.SZ 完成\n", + "任务 300975.SZ 完成\n", "任务 300976.SZ 完成\n", "任务 300977.SZ 完成\n", - "任务 300979.SZ 完成\n", "任务 300978.SZ 完成\n", + "任务 300979.SZ 完成\n", "任务 300980.SZ 完成\n", "任务 300981.SZ 完成\n", "任务 300982.SZ 完成\n", "任务 300983.SZ 完成\n", - "任务 300985.SZ 完成\n", "任务 300984.SZ 完成\n", - "任务 300987.SZ 完成\n", + "任务 300985.SZ 完成\n", "任务 300986.SZ 完成\n", + "任务 300987.SZ 完成\n", "任务 300988.SZ 完成\n", "任务 300989.SZ 完成\n", - "任务 300991.SZ 完成\n", "任务 300990.SZ 完成\n", - "任务 300993.SZ 完成\n", + "任务 300991.SZ 完成\n", "任务 300992.SZ 完成\n", + "任务 300993.SZ 完成\n", "任务 300994.SZ 完成\n", "任务 300995.SZ 完成\n", "任务 300996.SZ 完成\n", @@ -2515,20 +2562,20 @@ "任务 300999.SZ 完成\n", "任务 301000.SZ 完成\n", "任务 301001.SZ 完成\n", - "任务 301003.SZ 完成\n", "任务 301002.SZ 完成\n", + "任务 301003.SZ 完成\n", "任务 301004.SZ 完成\n", "任务 301005.SZ 完成\n", - "任务 301007.SZ 完成\n", "任务 301006.SZ 完成\n", - "任务 301009.SZ 完成\n", + "任务 301007.SZ 完成\n", "任务 301008.SZ 完成\n", + "任务 301009.SZ 完成\n", "任务 301010.SZ 完成\n", "任务 301011.SZ 完成\n", - "任务 301012.SZ 完成\n", "任务 301013.SZ 完成\n", - "任务 301015.SZ 完成\n", + "任务 301012.SZ 完成\n", "任务 301016.SZ 完成\n", + "任务 301015.SZ 完成\n", "任务 301017.SZ 完成\n", "任务 301018.SZ 完成\n", "任务 301019.SZ 完成\n", @@ -2539,32 +2586,32 @@ "任务 301024.SZ 完成\n", "任务 301025.SZ 完成\n", "任务 301026.SZ 完成\n", - "任务 301028.SZ 完成\n", "任务 301027.SZ 完成\n", + "任务 301028.SZ 完成\n", "任务 301029.SZ 完成\n", "任务 301030.SZ 完成\n", "任务 301031.SZ 完成\n", "任务 301032.SZ 完成\n", "任务 301033.SZ 完成\n", "任务 301035.SZ 完成\n", - "任务 301037.SZ 完成\n", "任务 301036.SZ 完成\n", - "任务 301039.SZ 完成\n", + "任务 301037.SZ 完成\n", "任务 301038.SZ 完成\n", + "任务 301039.SZ 完成\n", "任务 301040.SZ 完成\n", "任务 301041.SZ 完成\n", "任务 301042.SZ 完成\n", "任务 301043.SZ 完成\n", - "任务 301046.SZ 完成\n", "任务 301045.SZ 完成\n", - "任务 301048.SZ 完成\n", + "任务 301046.SZ 完成\n", "任务 301047.SZ 完成\n", - "任务 301050.SZ 完成\n", + "任务 301048.SZ 完成\n", "任务 301049.SZ 完成\n", - "任务 301052.SZ 完成\n", + "任务 301050.SZ 完成\n", "任务 301051.SZ 完成\n", - "任务 301055.SZ 完成\n", + "任务 301052.SZ 完成\n", "任务 301053.SZ 完成\n", + "任务 301055.SZ 完成\n", "任务 301056.SZ 完成\n", "任务 301057.SZ 完成\n", "任务 301058.SZ 完成\n", @@ -2577,24 +2624,24 @@ "任务 301066.SZ 完成\n", "任务 301067.SZ 完成\n", "任务 301068.SZ 完成\n", - "任务 301070.SZ 完成\n", "任务 301069.SZ 完成\n", + "任务 301070.SZ 完成\n", "任务 301071.SZ 完成\n", "任务 301072.SZ 完成\n", - "任务 301075.SZ 完成\n", "任务 301073.SZ 完成\n", + "任务 301075.SZ 完成\n", "任务 301076.SZ 完成\n", "任务 301077.SZ 完成\n", - "任务 301079.SZ 完成\n", "任务 301078.SZ 完成\n", + "任务 301079.SZ 完成\n", "任务 301080.SZ 完成\n", "任务 301081.SZ 完成\n", - "任务 301083.SZ 完成\n", "任务 301082.SZ 完成\n", - "任务 301086.SZ 完成\n", + "任务 301083.SZ 完成\n", "任务 301085.SZ 完成\n", - "任务 301088.SZ 完成\n", + "任务 301086.SZ 完成\n", "任务 301087.SZ 完成\n", + "任务 301088.SZ 完成\n", "任务 301089.SZ 完成\n", "任务 301090.SZ 完成\n", "任务 301091.SZ 完成\n", @@ -2605,16 +2652,16 @@ "任务 301097.SZ 完成\n", "任务 301098.SZ 完成\n", "任务 301099.SZ 完成\n", - "任务 301101.SZ 完成\n", "任务 301100.SZ 完成\n", + "任务 301101.SZ 完成\n", "任务 301102.SZ 完成\n", "任务 301103.SZ 完成\n", "任务 301105.SZ 完成\n", "任务 301106.SZ 完成\n", "任务 301107.SZ 完成\n", "任务 301108.SZ 完成\n", - "任务 301110.SZ 完成\n", "任务 301109.SZ 完成\n", + "任务 301110.SZ 完成\n", "任务 301111.SZ 完成\n", "任务 301112.SZ 完成\n", "任务 301113.SZ 完成\n", @@ -2698,8 +2745,8 @@ "任务 301208.SZ 完成\n", "任务 301209.SZ 完成\n", "任务 301210.SZ 完成\n", - "任务 301211.SZ 完成\n", "任务 301212.SZ 完成\n", + "任务 301211.SZ 完成\n", "任务 301213.SZ 完成\n", "任务 301215.SZ 完成\n", "任务 301216.SZ 完成\n", @@ -2747,12 +2794,12 @@ "任务 301272.SZ 完成\n", "任务 301273.SZ 完成\n", "任务 301276.SZ 完成\n", - "任务 301278.SZ 完成\n", "任务 301277.SZ 完成\n", - "任务 301280.SZ 完成\n", + "任务 301278.SZ 完成\n", "任务 301279.SZ 完成\n", - "任务 301282.SZ 完成\n", + "任务 301280.SZ 完成\n", "任务 301281.SZ 完成\n", + "任务 301282.SZ 完成\n", "任务 301283.SZ 完成\n", "任务 301285.SZ 完成\n", "任务 301286.SZ 完成\n", @@ -2865,8 +2912,8 @@ "任务 301469.SZ 完成\n", "任务 301486.SZ 完成\n", "任务 301487.SZ 完成\n", - "任务 301489.SZ 完成\n", "任务 301488.SZ 完成\n", + "任务 301489.SZ 完成\n", "任务 301498.SZ 完成\n", "任务 301499.SZ 完成\n", "任务 301500.SZ 完成\n", @@ -2893,8 +2940,8 @@ "任务 301533.SZ 完成\n", "任务 301536.SZ 完成\n", "任务 301538.SZ 完成\n", - "任务 301548.SZ 完成\n", "任务 301539.SZ 完成\n", + "任务 301548.SZ 完成\n", "任务 301550.SZ 完成\n", "任务 301551.SZ 完成\n", "任务 301552.SZ 完成\n", @@ -2987,10 +3034,10 @@ "任务 600048.SH 完成\n", "任务 600050.SH 完成\n", "任务 600051.SH 完成\n", - "任务 600053.SH 完成\n", "任务 600052.SH 完成\n", - "任务 600055.SH 完成\n", + "任务 600053.SH 完成\n", "任务 600054.SH 完成\n", + "任务 600055.SH 完成\n", "任务 600056.SH 完成\n", "任务 600057.SH 完成\n", "任务 600058.SH 完成\n", @@ -3001,10 +3048,10 @@ "任务 600063.SH 完成\n", "任务 600064.SH 完成\n", "任务 600066.SH 完成\n", - "任务 600070.SH 完成\n", "任务 600067.SH 完成\n", - "任务 600072.SH 完成\n", + "任务 600070.SH 完成\n", "任务 600071.SH 完成\n", + "任务 600072.SH 完成\n", "任务 600073.SH 完成\n", "任务 600075.SH 完成\n", "任务 600076.SH 完成\n", @@ -3029,16 +3076,16 @@ "任务 600103.SH 完成\n", "任务 600104.SH 完成\n", "任务 600105.SH 完成\n", - "任务 600107.SH 完成\n", "任务 600106.SH 完成\n", + "任务 600107.SH 完成\n", "任务 600108.SH 完成\n", "任务 600109.SH 完成\n", "任务 600110.SH 完成\n", "任务 600111.SH 完成\n", - "任务 600114.SH 完成\n", "任务 600113.SH 完成\n", - "任务 600116.SH 完成\n", + "任务 600114.SH 完成\n", "任务 600115.SH 完成\n", + "任务 600116.SH 完成\n", "任务 600117.SH 完成\n", "任务 600118.SH 完成\n", "任务 600119.SH 完成\n", @@ -3090,24 +3137,24 @@ "任务 600179.SH 完成\n", "任务 600180.SH 完成\n", "任务 600182.SH 完成\n", - "任务 600184.SH 完成\n", "任务 600183.SH 完成\n", + "任务 600184.SH 完成\n", "任务 600185.SH 完成\n", "任务 600186.SH 完成\n", "任务 600187.SH 完成\n", "任务 600188.SH 完成\n", - "任务 600190.SH 完成\n", "任务 600189.SH 完成\n", - "任务 600192.SH 完成\n", + "任务 600190.SH 完成\n", "任务 600191.SH 完成\n", - "任务 600195.SH 完成\n", + "任务 600192.SH 完成\n", "任务 600193.SH 完成\n", + "任务 600195.SH 完成\n", "任务 600196.SH 完成\n", "任务 600197.SH 完成\n", "任务 600198.SH 完成\n", "任务 600199.SH 完成\n", - "任务 600201.SH 完成\n", "任务 600200.SH 完成\n", + "任务 600201.SH 完成\n", "任务 600202.SH 完成\n", "任务 600203.SH 完成\n", "任务 600206.SH 完成\n", @@ -3130,16 +3177,16 @@ "任务 600228.SH 完成\n", "任务 600229.SH 完成\n", "任务 600230.SH 完成\n", - "任务 600232.SH 完成\n", "任务 600231.SH 完成\n", + "任务 600232.SH 完成\n", "任务 600233.SH 完成\n", "任务 600234.SH 完成\n", - "任务 600236.SH 完成\n", "任务 600235.SH 完成\n", + "任务 600236.SH 完成\n", "任务 600237.SH 完成\n", "任务 600238.SH 完成\n", - "任务 600241.SH 完成\n", "任务 600239.SH 完成\n", + "任务 600241.SH 完成\n", "任务 600243.SH 完成\n", "任务 600246.SH 完成\n", "任务 600248.SH 完成\n", @@ -3178,10 +3225,10 @@ "任务 600293.SH 完成\n", "任务 600295.SH 完成\n", "任务 600298.SH 完成\n", - "任务 600300.SH 完成\n", "任务 600299.SH 完成\n", - "任务 600302.SH 完成\n", + "任务 600300.SH 完成\n", "任务 600301.SH 完成\n", + "任务 600302.SH 完成\n", "任务 600303.SH 完成\n", "任务 600305.SH 完成\n", "任务 600307.SH 完成\n", @@ -3220,60 +3267,60 @@ "任务 600351.SH 完成\n", "任务 600352.SH 完成\n", "任务 600353.SH 完成\n", - "任务 600355.SH 完成\n", "任务 600354.SH 完成\n", - "任务 600358.SH 完成\n", + "任务 600355.SH 完成\n", "任务 600356.SH 完成\n", + "任务 600358.SH 完成\n", "任务 600359.SH 完成\n", "任务 600360.SH 完成\n", "任务 600361.SH 完成\n", "任务 600362.SH 完成\n", - "任务 600365.SH 完成\n", "任务 600363.SH 完成\n", - "任务 600367.SH 完成\n", + "任务 600365.SH 完成\n", "任务 600366.SH 完成\n", + "任务 600367.SH 完成\n", "任务 600368.SH 完成\n", "任务 600369.SH 完成\n", "任务 600370.SH 完成\n", "任务 600371.SH 完成\n", - "任务 600373.SH 完成\n", "任务 600372.SH 完成\n", + "任务 600373.SH 完成\n", "任务 600375.SH 完成\n", "任务 600376.SH 完成\n", - "任务 600378.SH 完成\n", "任务 600377.SH 完成\n", + "任务 600378.SH 完成\n", "任务 600379.SH 完成\n", "任务 600380.SH 完成\n", - "任务 600381.SH 完成\n", "任务 600382.SH 完成\n", - "任务 600383.SH 完成\n", + "任务 600381.SH 完成\n", "任务 600386.SH 完成\n", - "任务 600387.SH 完成\n", + "任务 600383.SH 完成\n", "任务 600388.SH 完成\n", + "任务 600387.SH 完成\n", "任务 600389.SH 完成\n", "任务 600390.SH 完成\n", "任务 600391.SH 完成\n", "任务 600392.SH 完成\n", - "任务 600395.SH 完成\n", "任务 600396.SH 完成\n", + "任务 600395.SH 完成\n", "任务 600397.SH 完成\n", "任务 600398.SH 完成\n", "任务 600399.SH 完成\n", "任务 600400.SH 完成\n", - "任务 600403.SH 完成\n", "任务 600405.SH 完成\n", - "任务 600406.SH 完成\n", + "任务 600403.SH 完成\n", "任务 600408.SH 完成\n", + "任务 600406.SH 完成\n", "任务 600409.SH 完成\n", "任务 600410.SH 完成\n", "任务 600415.SH 完成\n", "任务 600416.SH 完成\n", - "任务 600418.SH 完成\n", "任务 600419.SH 完成\n", - "任务 600420.SH 完成\n", + "任务 600418.SH 完成\n", "任务 600421.SH 完成\n", - "任务 600422.SH 完成\n", + "任务 600420.SH 完成\n", "任务 600423.SH 完成\n", + "任务 600422.SH 完成\n", "任务 600425.SH 完成\n", "任务 600426.SH 完成\n", "任务 600428.SH 完成\n", @@ -3325,8 +3372,8 @@ "任务 600501.SH 完成\n", "任务 600502.SH 完成\n", "任务 600503.SH 完成\n", - "任务 600506.SH 完成\n", "任务 600505.SH 完成\n", + "任务 600506.SH 完成\n", "任务 600507.SH 完成\n", "任务 600508.SH 完成\n", "任务 600509.SH 完成\n", @@ -3361,6 +3408,7 @@ "任务 600545.SH 完成\n", "任务 600546.SH 完成\n", "任务 600547.SH 完成\n", + "任务 600548.SH 完成\n", "任务 600549.SH 完成\n", "任务 600550.SH 完成\n", "任务 600551.SH 完成\n", @@ -3393,8 +3441,8 @@ "任务 600584.SH 完成\n", "任务 600585.SH 完成\n", "任务 600586.SH 完成\n", - "任务 600587.SH 完成\n", "任务 600588.SH 完成\n", + "任务 600587.SH 完成\n", "任务 600589.SH 完成\n", "任务 600590.SH 完成\n", "任务 600592.SH 完成\n", @@ -3417,16 +3465,15 @@ "任务 600610.SH 完成\n", "任务 600611.SH 完成\n", "任务 600612.SH 完成\n", - "任务 600548.SH 完成\n", "任务 600613.SH 完成\n", "任务 600615.SH 完成\n", "任务 600616.SH 完成\n", - "任务 600618.SH 完成\n", "任务 600617.SH 完成\n", - "任务 600620.SH 完成\n", + "任务 600618.SH 完成\n", "任务 600619.SH 完成\n", - "任务 600622.SH 完成\n", + "任务 600620.SH 完成\n", "任务 600621.SH 完成\n", + "任务 600622.SH 完成\n", "任务 600623.SH 完成\n", "任务 600624.SH 完成\n", "任务 600626.SH 完成\n", @@ -3435,8 +3482,8 @@ "任务 600630.SH 完成\n", "任务 600633.SH 完成\n", "任务 600635.SH 完成\n", - "任务 600637.SH 完成\n", "任务 600636.SH 完成\n", + "任务 600637.SH 完成\n", "任务 600638.SH 完成\n", "任务 600639.SH 完成\n", "任务 600640.SH 完成\n", @@ -3496,14 +3543,14 @@ "任务 600708.SH 完成\n", "任务 600710.SH 完成\n", "任务 600711.SH 完成\n", - "任务 600713.SH 完成\n", "任务 600712.SH 完成\n", + "任务 600713.SH 完成\n", "任务 600714.SH 完成\n", "任务 600715.SH 完成\n", "任务 600716.SH 完成\n", "任务 600717.SH 完成\n", - "任务 600719.SH 完成\n", "任务 600718.SH 完成\n", + "任务 600719.SH 完成\n", "任务 600720.SH 完成\n", "任务 600721.SH 完成\n", "任务 600722.SH 完成\n", @@ -3524,8 +3571,8 @@ "任务 600738.SH 完成\n", "任务 600739.SH 完成\n", "任务 600740.SH 完成\n", - "任务 600742.SH 完成\n", "任务 600741.SH 完成\n", + "任务 600742.SH 完成\n", "任务 600743.SH 完成\n", "任务 600744.SH 完成\n", "任务 600745.SH 完成\n", @@ -3597,8 +3644,8 @@ "任务 600824.SH 完成\n", "任务 600825.SH 完成\n", "任务 600826.SH 完成\n", - "任务 600828.SH 完成\n", "任务 600827.SH 完成\n", + "任务 600828.SH 完成\n", "任务 600829.SH 完成\n", "任务 600830.SH 完成\n", "任务 600831.SH 完成\n", @@ -3623,30 +3670,30 @@ "任务 600857.SH 完成\n", "任务 600858.SH 完成\n", "任务 600859.SH 完成\n", - "任务 600861.SH 完成\n", "任务 600860.SH 完成\n", + "任务 600861.SH 完成\n", "任务 600862.SH 完成\n", "任务 600863.SH 完成\n", - "任务 600865.SH 完成\n", "任务 600864.SH 完成\n", - "任务 600866.SH 完成\n", + "任务 600865.SH 完成\n", "任务 600867.SH 完成\n", - "任务 600869.SH 完成\n", + "任务 600866.SH 完成\n", "任务 600868.SH 完成\n", - "任务 600872.SH 完成\n", + "任务 600869.SH 完成\n", "任务 600871.SH 完成\n", - "任务 600873.SH 完成\n", + "任务 600872.SH 完成\n", "任务 600874.SH 完成\n", - "任务 600875.SH 完成\n", + "任务 600873.SH 完成\n", "任务 600876.SH 完成\n", - "任务 600877.SH 完成\n", + "任务 600875.SH 完成\n", "任务 600879.SH 完成\n", + "任务 600877.SH 完成\n", "任务 600880.SH 完成\n", "任务 600881.SH 完成\n", - "任务 600882.SH 完成\n", "任务 600883.SH 完成\n", - "任务 600884.SH 完成\n", + "任务 600882.SH 完成\n", "任务 600885.SH 完成\n", + "任务 600884.SH 完成\n", "任务 600886.SH 完成\n", "任务 600887.SH 完成\n", "任务 600888.SH 完成\n", @@ -3655,20 +3702,20 @@ "任务 600893.SH 完成\n", "任务 600894.SH 完成\n", "任务 600895.SH 完成\n", - "任务 600897.SH 完成\n", "任务 600898.SH 完成\n", - "任务 600900.SH 完成\n", + "任务 600897.SH 完成\n", "任务 600901.SH 完成\n", - "任务 600903.SH 完成\n", + "任务 600900.SH 完成\n", "任务 600905.SH 完成\n", - "任务 600906.SH 完成\n", + "任务 600903.SH 完成\n", "任务 600908.SH 完成\n", + "任务 600906.SH 完成\n", "任务 600909.SH 完成\n", "任务 600916.SH 完成\n", "任务 600917.SH 完成\n", "任务 600918.SH 完成\n", - "任务 600919.SH 完成\n", "任务 600925.SH 完成\n", + "任务 600919.SH 完成\n", "任务 600926.SH 完成\n", "任务 600927.SH 完成\n", "任务 600928.SH 完成\n", @@ -3689,18 +3736,18 @@ "任务 600963.SH 完成\n", "任务 600965.SH 完成\n", "任务 600966.SH 完成\n", - "任务 600968.SH 完成\n", "任务 600967.SH 完成\n", + "任务 600968.SH 完成\n", "任务 600969.SH 完成\n", "任务 600970.SH 完成\n", "任务 600971.SH 完成\n", "任务 600973.SH 完成\n", - "任务 600976.SH 完成\n", "任务 600975.SH 完成\n", + "任务 600976.SH 完成\n", "任务 600977.SH 完成\n", "任务 600979.SH 完成\n", - "任务 600981.SH 完成\n", "任务 600980.SH 完成\n", + "任务 600981.SH 完成\n", "任务 600982.SH 完成\n", "任务 600983.SH 完成\n", "任务 600984.SH 完成\n", @@ -3789,24 +3836,24 @@ "任务 601198.SH 完成\n", "任务 601199.SH 完成\n", "任务 601200.SH 完成\n", - "任务 601208.SH 完成\n", "任务 601211.SH 完成\n", + "任务 601208.SH 完成\n", "任务 601212.SH 完成\n", "任务 601216.SH 完成\n", "任务 601218.SH 完成\n", "任务 601222.SH 完成\n", "任务 601225.SH 完成\n", "任务 601226.SH 完成\n", - "任务 601228.SH 完成\n", "任务 601229.SH 完成\n", + "任务 601228.SH 完成\n", "任务 601231.SH 完成\n", "任务 601233.SH 完成\n", "任务 601236.SH 完成\n", "任务 601238.SH 完成\n", - "任务 601279.SH 完成\n", "任务 601288.SH 完成\n", - "任务 601298.SH 完成\n", + "任务 601279.SH 完成\n", "任务 601311.SH 完成\n", + "任务 601298.SH 完成\n", "任务 601318.SH 完成\n", "任务 601319.SH 完成\n", "任务 601326.SH 完成\n", @@ -3888,8 +3935,8 @@ "任务 601811.SH 完成\n", "任务 601816.SH 完成\n", "任务 601818.SH 完成\n", - "任务 601825.SH 完成\n", "任务 601827.SH 完成\n", + "任务 601825.SH 完成\n", "任务 601828.SH 完成\n", "任务 601838.SH 完成\n", "任务 601857.SH 完成\n", @@ -3900,8 +3947,8 @@ "任务 601868.SH 完成\n", "任务 601869.SH 完成\n", "任务 601872.SH 完成\n", - "任务 601877.SH 完成\n", "任务 601878.SH 完成\n", + "任务 601877.SH 完成\n", "任务 601880.SH 完成\n", "任务 601881.SH 完成\n", "任务 601882.SH 完成\n", @@ -3939,8 +3986,8 @@ "任务 601992.SH 完成\n", "任务 601995.SH 完成\n", "任务 601996.SH 完成\n", - "任务 601998.SH 完成\n", "任务 601997.SH 完成\n", + "任务 601998.SH 完成\n", "任务 601999.SH 完成\n", "任务 603000.SH 完成\n", "任务 603001.SH 完成\n", @@ -4077,28 +4124,28 @@ "任务 603165.SH 完成\n", "任务 603166.SH 完成\n", "任务 603167.SH 完成\n", - "任务 603169.SH 完成\n", "任务 603168.SH 完成\n", - "任务 603171.SH 完成\n", + "任务 603169.SH 完成\n", "任务 603170.SH 完成\n", + "任务 603171.SH 完成\n", "任务 603172.SH 完成\n", "任务 603173.SH 完成\n", - "任务 603177.SH 完成\n", "任务 603176.SH 完成\n", - "任务 603179.SH 完成\n", + "任务 603177.SH 完成\n", "任务 603178.SH 完成\n", - "任务 603181.SH 完成\n", + "任务 603179.SH 完成\n", "任务 603180.SH 完成\n", - "任务 603183.SH 完成\n", + "任务 603181.SH 完成\n", "任务 603182.SH 完成\n", + "任务 603183.SH 完成\n", "任务 603185.SH 完成\n", "任务 603186.SH 完成\n", "任务 603187.SH 完成\n", "任务 603188.SH 完成\n", "任务 603189.SH 完成\n", "任务 603190.SH 完成\n", - "任务 603192.SH 完成\n", "任务 603191.SH 完成\n", + "任务 603192.SH 完成\n", "任务 603193.SH 完成\n", "任务 603194.SH 完成\n", "任务 603195.SH 完成\n", @@ -4197,10 +4244,10 @@ "任务 603324.SH 完成\n", "任务 603325.SH 完成\n", "任务 603326.SH 完成\n", - "任务 603327.SH 完成\n", "任务 603328.SH 完成\n", - "任务 603329.SH 完成\n", + "任务 603327.SH 完成\n", "任务 603330.SH 完成\n", + "任务 603329.SH 完成\n", "任务 603331.SH 完成\n", "任务 603332.SH 完成\n", "任务 603333.SH 完成\n", @@ -4318,16 +4365,16 @@ "任务 603607.SH 完成\n", "任务 603608.SH 完成\n", "任务 603609.SH 完成\n", - "任务 603610.SH 完成\n", "任务 603611.SH 完成\n", + "任务 603610.SH 完成\n", "任务 603612.SH 完成\n", "任务 603613.SH 完成\n", "任务 603615.SH 完成\n", "任务 603616.SH 完成\n", - "任务 603617.SH 完成\n", "任务 603618.SH 完成\n", - "任务 603619.SH 完成\n", + "任务 603617.SH 完成\n", "任务 603626.SH 完成\n", + "任务 603619.SH 完成\n", "任务 603628.SH 完成\n", "任务 603629.SH 完成\n", "任务 603630.SH 完成\n", @@ -4342,16 +4389,16 @@ "任务 603656.SH 完成\n", "任务 603657.SH 完成\n", "任务 603658.SH 完成\n", - "任务 603660.SH 完成\n", "任务 603659.SH 完成\n", + "任务 603660.SH 完成\n", "任务 603661.SH 完成\n", "任务 603662.SH 完成\n", "任务 603663.SH 完成\n", "任务 603665.SH 完成\n", "任务 603666.SH 完成\n", "任务 603667.SH 完成\n", - "任务 603669.SH 完成\n", "任务 603668.SH 完成\n", + "任务 603669.SH 完成\n", "任务 603676.SH 完成\n", "任务 603677.SH 完成\n", "任务 603678.SH 完成\n", @@ -4362,10 +4409,10 @@ "任务 603683.SH 完成\n", "任务 603685.SH 完成\n", "任务 603686.SH 完成\n", - "任务 603688.SH 完成\n", "任务 603687.SH 完成\n", - "任务 603690.SH 完成\n", + "任务 603688.SH 完成\n", "任务 603689.SH 完成\n", + "任务 603690.SH 完成\n", "任务 603693.SH 完成\n", "任务 603696.SH 完成\n", "任务 603697.SH 完成\n", @@ -4374,34 +4421,34 @@ "任务 603700.SH 完成\n", "任务 603701.SH 完成\n", "任务 603703.SH 完成\n", - "任务 603707.SH 完成\n", "任务 603706.SH 完成\n", - "任务 603709.SH 完成\n", + "任务 603707.SH 完成\n", "任务 603708.SH 完成\n", - "任务 603711.SH 完成\n", + "任务 603709.SH 完成\n", "任务 603712.SH 完成\n", + "任务 603711.SH 完成\n", "任务 603713.SH 完成\n", "任务 603716.SH 完成\n", - "任务 603718.SH 完成\n", "任务 603717.SH 完成\n", + "任务 603718.SH 完成\n", "任务 603719.SH 完成\n", "任务 603721.SH 完成\n", - "任务 603722.SH 完成\n", "任务 603725.SH 完成\n", - "任务 603727.SH 完成\n", + "任务 603722.SH 完成\n", "任务 603726.SH 完成\n", - "任务 603729.SH 完成\n", + "任务 603727.SH 完成\n", "任务 603728.SH 完成\n", + "任务 603729.SH 完成\n", "任务 603730.SH 完成\n", "任务 603733.SH 完成\n", - "任务 603738.SH 完成\n", "任务 603737.SH 完成\n", + "任务 603738.SH 完成\n", "任务 603739.SH 完成\n", "任务 603755.SH 完成\n", "任务 603757.SH 完成\n", "任务 603758.SH 完成\n", - "任务 603766.SH 完成\n", "任务 603759.SH 完成\n", + "任务 603766.SH 完成\n", "任务 603767.SH 完成\n", "任务 603768.SH 完成\n", "任务 603773.SH 完成\n", @@ -4410,16 +4457,16 @@ "任务 603778.SH 完成\n", "任务 603779.SH 完成\n", "任务 603786.SH 完成\n", - "任务 603788.SH 完成\n", "任务 603787.SH 完成\n", - "任务 603790.SH 完成\n", + "任务 603788.SH 完成\n", "任务 603789.SH 完成\n", + "任务 603790.SH 完成\n", "任务 603797.SH 完成\n", "任务 603798.SH 完成\n", "任务 603799.SH 完成\n", "任务 603800.SH 完成\n", - "任务 603803.SH 完成\n", "任务 603801.SH 完成\n", + "任务 603803.SH 完成\n", "任务 603806.SH 完成\n", "任务 603808.SH 完成\n", "任务 603809.SH 完成\n", @@ -4496,8 +4543,8 @@ "任务 603931.SH 完成\n", "任务 603933.SH 完成\n", "任务 603936.SH 完成\n", - "任务 603937.SH 完成\n", "任务 603938.SH 完成\n", + "任务 603937.SH 完成\n", "任务 603939.SH 完成\n", "任务 603948.SH 完成\n", "任务 603949.SH 完成\n", @@ -4567,8 +4614,8 @@ "任务 605108.SH 完成\n", "任务 605111.SH 完成\n", "任务 605116.SH 完成\n", - "任务 605117.SH 完成\n", "任务 605118.SH 完成\n", + "任务 605117.SH 完成\n", "任务 605122.SH 完成\n", "任务 605123.SH 完成\n", "任务 605128.SH 完成\n", @@ -4579,20 +4626,20 @@ "任务 605155.SH 完成\n", "任务 605158.SH 完成\n", "任务 605162.SH 完成\n", - "任务 605166.SH 完成\n", "任务 605167.SH 完成\n", + "任务 605166.SH 完成\n", "任务 605168.SH 完成\n", "任务 605169.SH 完成\n", "任务 605177.SH 完成\n", "任务 605178.SH 完成\n", "任务 605179.SH 完成\n", "任务 605180.SH 完成\n", - "任务 605183.SH 完成\n", "任务 605186.SH 完成\n", - "任务 605188.SH 完成\n", + "任务 605183.SH 完成\n", "任务 605189.SH 完成\n", - "任务 605196.SH 完成\n", + "任务 605188.SH 完成\n", "任务 605198.SH 完成\n", + "任务 605196.SH 完成\n", "任务 605199.SH 完成\n", "任务 605208.SH 完成\n", "任务 605218.SH 完成\n", @@ -4605,8 +4652,8 @@ "任务 605268.SH 完成\n", "任务 605277.SH 完成\n", "任务 605286.SH 完成\n", - "任务 605287.SH 完成\n", "任务 605288.SH 完成\n", + "任务 605287.SH 完成\n", "任务 605289.SH 完成\n", "任务 605296.SH 完成\n", "任务 605298.SH 完成\n", @@ -4615,8 +4662,8 @@ "任务 605303.SH 完成\n", "任务 605305.SH 完成\n", "任务 605318.SH 完成\n", - "任务 605319.SH 完成\n", "任务 605333.SH 完成\n", + "任务 605319.SH 完成\n", "任务 605336.SH 完成\n", "任务 605337.SH 完成\n", "任务 605338.SH 完成\n", @@ -4699,24 +4746,24 @@ "任务 688059.SH 完成\n", "任务 688060.SH 完成\n", "任务 688061.SH 完成\n", - "任务 688062.SH 完成\n", "任务 688063.SH 完成\n", + "任务 688062.SH 完成\n", "任务 688065.SH 完成\n", "任务 688066.SH 完成\n", "任务 688067.SH 完成\n", "任务 688068.SH 完成\n", "任务 688069.SH 完成\n", "任务 688070.SH 完成\n", - "任务 688071.SH 完成\n", "任务 688072.SH 完成\n", - "任务 688073.SH 完成\n", + "任务 688071.SH 完成\n", "任务 688075.SH 完成\n", + "任务 688073.SH 完成\n", "任务 688076.SH 完成\n", "任务 688077.SH 完成\n", "任务 688078.SH 完成\n", "任务 688079.SH 完成\n", - "任务 688080.SH 完成\n", "任务 688081.SH 完成\n", + "任务 688080.SH 完成\n", "任务 688082.SH 完成\n", "任务 688083.SH 完成\n", "任务 688084.SH 完成\n", @@ -4737,16 +4784,16 @@ "任务 688101.SH 完成\n", "任务 688102.SH 完成\n", "任务 688103.SH 完成\n", - "任务 688105.SH 完成\n", "任务 688106.SH 完成\n", + "任务 688105.SH 完成\n", "任务 688107.SH 完成\n", "任务 688108.SH 完成\n", "任务 688109.SH 完成\n", "任务 688110.SH 完成\n", - "任务 688111.SH 完成\n", "任务 688112.SH 完成\n", - "任务 688113.SH 完成\n", + "任务 688111.SH 完成\n", "任务 688114.SH 完成\n", + "任务 688113.SH 完成\n", "任务 688115.SH 完成\n", "任务 688116.SH 完成\n", "任务 688117.SH 完成\n", @@ -4765,34 +4812,34 @@ "任务 688131.SH 完成\n", "任务 688132.SH 完成\n", "任务 688133.SH 完成\n", - "任务 688136.SH 完成\n", "任务 688135.SH 完成\n", - "任务 688137.SH 完成\n", + "任务 688136.SH 完成\n", "任务 688138.SH 完成\n", + "任务 688137.SH 完成\n", "任务 688139.SH 完成\n", "任务 688141.SH 完成\n", - "任务 688146.SH 完成\n", "任务 688143.SH 完成\n", + "任务 688146.SH 完成\n", "任务 688147.SH 完成\n", "任务 688148.SH 完成\n", - "任务 688150.SH 完成\n", "任务 688151.SH 完成\n", + "任务 688150.SH 完成\n", "任务 688152.SH 完成\n", "任务 688153.SH 完成\n", - "任务 688155.SH 完成\n", "任务 688156.SH 完成\n", - "任务 688157.SH 完成\n", + "任务 688155.SH 完成\n", "任务 688158.SH 完成\n", + "任务 688157.SH 完成\n", "任务 688159.SH 完成\n", "任务 688160.SH 完成\n", "任务 688161.SH 完成\n", "任务 688162.SH 完成\n", - "任务 688163.SH 完成\n", "任务 688165.SH 完成\n", + "任务 688163.SH 完成\n", "任务 688167.SH 完成\n", "任务 688166.SH 完成\n", - "任务 688168.SH 完成\n", "任务 688169.SH 完成\n", + "任务 688168.SH 完成\n", "任务 688170.SH 完成\n", "任务 688171.SH 完成\n", "任务 688172.SH 完成\n", @@ -4807,10 +4854,10 @@ "任务 688182.SH 完成\n", "任务 688183.SH 完成\n", "任务 688184.SH 完成\n", - "任务 688186.SH 完成\n", "任务 688185.SH 完成\n", - "任务 688188.SH 完成\n", + "任务 688186.SH 完成\n", "任务 688187.SH 完成\n", + "任务 688188.SH 完成\n", "任务 688189.SH 完成\n", "任务 688190.SH 完成\n", "任务 688191.SH 完成\n", @@ -4863,16 +4910,16 @@ "任务 688247.SH 完成\n", "任务 688248.SH 完成\n", "任务 688249.SH 完成\n", - "任务 688252.SH 完成\n", "任务 688251.SH 完成\n", - "任务 688255.SH 完成\n", + "任务 688252.SH 完成\n", "任务 688253.SH 完成\n", - "任务 688257.SH 完成\n", + "任务 688255.SH 完成\n", "任务 688256.SH 完成\n", - "任务 688259.SH 完成\n", + "任务 688257.SH 完成\n", "任务 688258.SH 完成\n", - "任务 688261.SH 完成\n", + "任务 688259.SH 完成\n", "任务 688260.SH 完成\n", + "任务 688261.SH 完成\n", "任务 688262.SH 完成\n", "任务 688265.SH 完成\n", "任务 688266.SH 完成\n", @@ -4901,8 +4948,8 @@ "任务 688291.SH 完成\n", "任务 688292.SH 完成\n", "任务 688293.SH 完成\n", - "任务 688295.SH 完成\n", "任务 688296.SH 完成\n", + "任务 688295.SH 完成\n", "任务 688297.SH 完成\n", "任务 688298.SH 完成\n", "任务 688299.SH 完成\n", @@ -4915,8 +4962,8 @@ "任务 688307.SH 完成\n", "任务 688308.SH 完成\n", "任务 688309.SH 完成\n", - "任务 688310.SH 完成\n", "任务 688311.SH 完成\n", + "任务 688310.SH 完成\n", "任务 688312.SH 完成\n", "任务 688313.SH 完成\n", "任务 688314.SH 完成\n", @@ -4925,8 +4972,8 @@ "任务 688317.SH 完成\n", "任务 688318.SH 完成\n", "任务 688319.SH 完成\n", - "任务 688320.SH 完成\n", "任务 688321.SH 完成\n", + "任务 688320.SH 完成\n", "任务 688322.SH 完成\n", "任务 688323.SH 完成\n", "任务 688325.SH 完成\n", @@ -5009,12 +5056,12 @@ "任务 688428.SH 完成\n", "任务 688429.SH 完成\n", "任务 688432.SH 完成\n", - "任务 688435.SH 完成\n", "任务 688433.SH 完成\n", - "任务 688443.SH 完成\n", + "任务 688435.SH 完成\n", "任务 688439.SH 完成\n", - "任务 688449.SH 完成\n", + "任务 688443.SH 完成\n", "任务 688448.SH 完成\n", + "任务 688449.SH 完成\n", "任务 688450.SH 完成\n", "任务 688455.SH 完成\n", "任务 688456.SH 完成\n", @@ -5039,12 +5086,12 @@ "任务 688500.SH 完成\n", "任务 688501.SH 完成\n", "任务 688502.SH 完成\n", - "任务 688505.SH 完成\n", "任务 688503.SH 完成\n", + "任务 688505.SH 完成\n", "任务 688506.SH 完成\n", "任务 688507.SH 完成\n", - "任务 688509.SH 完成\n", "任务 688508.SH 完成\n", + "任务 688509.SH 完成\n", "任务 688510.SH 完成\n", "任务 688511.SH 完成\n", "任务 688512.SH 完成\n", @@ -5077,22 +5124,22 @@ "任务 688551.SH 完成\n", "任务 688552.SH 完成\n", "任务 688553.SH 完成\n", - "任务 688557.SH 完成\n", "任务 688556.SH 完成\n", + "任务 688557.SH 完成\n", "任务 688558.SH 完成\n", "任务 688559.SH 完成\n", - "任务 688561.SH 完成\n", "任务 688560.SH 完成\n", + "任务 688561.SH 完成\n", "任务 688562.SH 完成\n", "任务 688563.SH 完成\n", - "任务 688566.SH 完成\n", "任务 688565.SH 完成\n", - "任务 688568.SH 完成\n", + "任务 688566.SH 完成\n", "任务 688567.SH 完成\n", - "任务 688570.SH 完成\n", + "任务 688568.SH 完成\n", "任务 688569.SH 完成\n", - "任务 688573.SH 完成\n", + "任务 688570.SH 完成\n", "任务 688571.SH 完成\n", + "任务 688573.SH 完成\n", "任务 688575.SH 完成\n", "任务 688576.SH 完成\n", "任务 688577.SH 完成\n", @@ -5105,12 +5152,12 @@ "任务 688584.SH 完成\n", "任务 688585.SH 完成\n", "任务 688586.SH 完成\n", - "任务 688589.SH 完成\n", "任务 688588.SH 完成\n", + "任务 688589.SH 完成\n", "任务 688590.SH 完成\n", "任务 688591.SH 完成\n", - "任务 688593.SH 完成\n", "任务 688592.SH 完成\n", + "任务 688593.SH 完成\n", "任务 688595.SH 完成\n", "任务 688596.SH 完成\n", "任务 688597.SH 完成\n", @@ -5119,19 +5166,19 @@ "任务 688600.SH 完成\n", "任务 688601.SH 完成\n", "任务 688602.SH 完成\n", - "任务 688605.SH 完成\n", "任务 688603.SH 完成\n", + "任务 688605.SH 完成\n", "任务 688606.SH 完成\n", "任务 688607.SH 完成\n", - "任务 688609.SH 完成\n", "任务 688608.SH 完成\n", + "任务 688609.SH 完成\n", "任务 688610.SH 完成\n", - "任务 688611.SH 完成\n", "任务 688612.SH 完成\n", + "任务 688611.SH 完成\n", "任务 688613.SH 完成\n", "任务 688615.SH 完成\n", - "任务 688616.SH 完成\n", "任务 688617.SH 完成\n", + "任务 688616.SH 完成\n", "任务 688618.SH 完成\n", "任务 688619.SH 完成\n", "任务 688620.SH 完成\n", @@ -5170,10 +5217,10 @@ "任务 688670.SH 完成\n", "任务 688671.SH 完成\n", "任务 688676.SH 完成\n", - "任务 688678.SH 完成\n", "任务 688677.SH 完成\n", - "任务 688680.SH 完成\n", + "任务 688678.SH 完成\n", "任务 688679.SH 完成\n", + "任务 688680.SH 完成\n", "任务 688681.SH 完成\n", "任务 688682.SH 完成\n", "任务 688683.SH 完成\n", @@ -5188,8 +5235,8 @@ "任务 688695.SH 完成\n", "任务 688696.SH 完成\n", "任务 688697.SH 完成\n", - "任务 688698.SH 完成\n", "任务 688699.SH 完成\n", + "任务 688698.SH 完成\n", "任务 688700.SH 完成\n", "任务 688701.SH 完成\n", "任务 688702.SH 完成\n", @@ -5198,20 +5245,20 @@ "任务 688709.SH 完成\n", "任务 688710.SH 完成\n", "任务 688711.SH 完成\n", - "任务 688716.SH 完成\n", "任务 688717.SH 完成\n", + "任务 688716.SH 完成\n", "任务 688718.SH 完成\n", "任务 688719.SH 完成\n", "任务 688720.SH 完成\n", "任务 688721.SH 完成\n", - "任务 688722.SH 完成\n", "任务 688726.SH 完成\n", + "任务 688722.SH 完成\n", "任务 688728.SH 完成\n", "任务 688733.SH 完成\n", - "任务 688737.SH 完成\n", "任务 688739.SH 完成\n", - "任务 688750.SH 完成\n", + "任务 688737.SH 完成\n", "任务 688758.SH 完成\n", + "任务 688750.SH 完成\n", "任务 688766.SH 完成\n", "任务 688767.SH 完成\n", "任务 688768.SH 完成\n", @@ -5222,8 +5269,8 @@ "任务 688779.SH 完成\n", "任务 688786.SH 完成\n", "任务 688787.SH 完成\n", - "任务 688788.SH 完成\n", "任务 688789.SH 完成\n", + "任务 688788.SH 完成\n", "任务 688793.SH 完成\n", "任务 688798.SH 完成\n", "任务 688799.SH 完成\n", @@ -5239,9 +5286,9 @@ "任务 830896.BJ 完成\n", "任务 830946.BJ 完成\n", "任务 830964.BJ 完成\n", - "任务 830974.BJ 完成\n", "任务 831010.BJ 完成\n", "任务 831039.BJ 完成\n", + "任务 830974.BJ 完成\n", "任务 831087.BJ 完成\n", "任务 831152.BJ 完成\n", "任务 831167.BJ 完成\n", @@ -5250,14 +5297,14 @@ "任务 831278.BJ 完成\n", "任务 831304.BJ 完成\n", "任务 831305.BJ 完成\n", - "任务 831396.BJ 完成\n", "任务 831370.BJ 完成\n", - "任务 831526.BJ 完成\n", + "任务 831396.BJ 完成\n", "任务 831445.BJ 完成\n", + "任务 831526.BJ 完成\n", "任务 831627.BJ 完成\n", "任务 831641.BJ 完成\n", - "任务 831726.BJ 完成\n", "任务 831689.BJ 完成\n", + "任务 831726.BJ 完成\n", "任务 831768.BJ 完成\n", "任务 831832.BJ 完成\n", "任务 831834.BJ 完成\n", @@ -5266,41 +5313,42 @@ "任务 831906.BJ 完成\n", "任务 831961.BJ 完成\n", "任务 832000.BJ 完成\n", - "任务 832089.BJ 完成\n", "任务 832023.BJ 完成\n", + "任务 832089.BJ 完成\n", "任务 832110.BJ 完成\n", "任务 832145.BJ 完成\n", "任务 832149.BJ 完成\n", "任务 832171.BJ 完成\n", "任务 832175.BJ 完成\n", "任务 832225.BJ 完成\n", - "任务 832419.BJ 完成\n", "任务 832278.BJ 完成\n", - "任务 832471.BJ 完成\n", + "任务 832419.BJ 完成\n", "任务 832469.BJ 完成\n", - "任务 832522.BJ 完成\n", + "任务 832471.BJ 完成\n", "任务 832491.BJ 完成\n", - "任务 832651.BJ 完成\n", + "任务 832522.BJ 完成\n", "任务 832566.BJ 完成\n", + "任务 832651.BJ 完成\n", "任务 832662.BJ 完成\n", "任务 832735.BJ 完成\n", - "任务 832802.BJ 完成\n", "任务 832786.BJ 完成\n", + "任务 832802.BJ 完成\n", "任务 832876.BJ 完成\n", "任务 832885.BJ 完成\n", "任务 832978.BJ 完成\n", "任务 832982.BJ 完成\n", + "任务 833030.BJ 完成\n", "任务 833075.BJ 完成\n", - "任务 833171.BJ 完成\n", "任务 833230.BJ 完成\n", - "任务 833266.BJ 完成\n", + "任务 833171.BJ 完成\n", "任务 833284.BJ 完成\n", - "任务 833346.BJ 完成\n", + "任务 833266.BJ 完成\n", "任务 833394.BJ 完成\n", - "任务 833427.BJ 完成\n", + "任务 833346.BJ 完成\n", "任务 833429.BJ 完成\n", - "任务 833454.BJ 完成\n", + "任务 833427.BJ 完成\n", "任务 833455.BJ 完成\n", + "任务 833454.BJ 完成\n", "任务 833509.BJ 完成\n", "任务 833523.BJ 完成\n", "任务 833533.BJ 完成\n", @@ -5342,9 +5390,8 @@ "任务 835670.BJ 完成\n", "任务 835857.BJ 完成\n", "任务 835892.BJ 完成\n", - "任务 835985.BJ 完成\n", - "任务 833030.BJ 完成\n", "任务 836077.BJ 完成\n", + "任务 835985.BJ 完成\n", "任务 836149.BJ 完成\n", "任务 836208.BJ 完成\n", "任务 836221.BJ 完成\n", @@ -5404,28 +5451,28 @@ "任务 839680.BJ 完成\n", "任务 839719.BJ 完成\n", "任务 839725.BJ 完成\n", - "任务 839790.BJ 完成\n", "任务 839729.BJ 完成\n", - "任务 839946.BJ 完成\n", + "任务 839790.BJ 完成\n", "任务 839792.BJ 完成\n", - "任务 870199.BJ 完成\n", + "任务 839946.BJ 完成\n", "任务 870204.BJ 完成\n", + "任务 870199.BJ 完成\n", "任务 870299.BJ 完成\n", "任务 870357.BJ 完成\n", "任务 870436.BJ 完成\n", "任务 870508.BJ 完成\n", - "任务 870656.BJ 完成\n", "任务 870726.BJ 完成\n", - "任务 870866.BJ 完成\n", + "任务 870656.BJ 完成\n", "任务 870976.BJ 完成\n", + "任务 870866.BJ 完成\n", "任务 871245.BJ 完成\n", "任务 871263.BJ 完成\n", "任务 871396.BJ 完成\n", "任务 871478.BJ 完成\n", "任务 871553.BJ 完成\n", "任务 871634.BJ 完成\n", - "任务 871642.BJ 完成\n", "任务 871694.BJ 完成\n", + "任务 871642.BJ 完成\n", "任务 871753.BJ 完成\n", "任务 871857.BJ 完成\n", "任务 871970.BJ 完成\n", @@ -5454,12 +5501,12 @@ "任务 873576.BJ 完成\n", "任务 873593.BJ 完成\n", "任务 873665.BJ 完成\n", - "任务 873679.BJ 完成\n", "任务 873690.BJ 完成\n", - "任务 873693.BJ 完成\n", + "任务 873679.BJ 完成\n", "任务 873703.BJ 完成\n", - "任务 873706.BJ 完成\n", + "任务 873693.BJ 完成\n", "任务 873726.BJ 完成\n", + "任务 873706.BJ 完成\n", "任务 873806.BJ 完成\n", "任务 873833.BJ 完成\n", "任务 920002.BJ 完成\n", @@ -5470,12 +5517,12 @@ "任务 920066.BJ 完成\n", "任务 920082.BJ 完成\n", "任务 920088.BJ 完成\n", - "任务 920098.BJ 完成\n", "任务 920099.BJ 完成\n", - "任务 920106.BJ 完成\n", + "任务 920098.BJ 完成\n", "任务 920108.BJ 完成\n", - "任务 920111.BJ 完成\n", + "任务 920106.BJ 完成\n", "任务 920116.BJ 完成\n", + "任务 920111.BJ 完成\n", "任务 920118.BJ 完成\n", "任务 920128.BJ 完成\n", "任务 689009.SH 完成\n", @@ -5494,35 +5541,35 @@ "任务 000405.SZ 完成\n", "任务 000406.SZ 完成\n", "任务 000412.SZ 完成\n", - "任务 000413.SZ 完成\n", "任务 000416.SZ 完成\n", - "任务 000418.SZ 完成\n", + "任务 000413.SZ 完成\n", "任务 000502.SZ 完成\n", - "任务 000508.SZ 完成\n", + "任务 000418.SZ 完成\n", "任务 000511.SZ 完成\n", - "任务 000515.SZ 完成\n", + "任务 000508.SZ 完成\n", "任务 000522.SZ 完成\n", - "任务 000527.SZ 完成\n", + "任务 000515.SZ 完成\n", "任务 000535.SZ 完成\n", + "任务 000527.SZ 完成\n", "任务 000540.SZ 完成\n", "任务 000542.SZ 完成\n", "任务 000549.SZ 完成\n", "任务 000556.SZ 完成\n", "任务 000562.SZ 完成\n", "任务 000569.SZ 完成\n", - "任务 000578.SZ 完成\n", "任务 000583.SZ 完成\n", - "任务 000585.SZ 完成\n", + "任务 000578.SZ 完成\n", "任务 000587.SZ 完成\n", + "任务 000585.SZ 完成\n", "任务 000588.SZ 完成\n", "任务 000594.SZ 完成\n", "任务 000602.SZ 完成\n", - "任务 000611.SZ 完成\n", "任务 000606.SZ 完成\n", + "任务 000611.SZ 完成\n", "任务 000613.SZ 完成\n", "任务 000616.SZ 完成\n", - "任务 000621.SZ 完成\n", "任务 000618.SZ 完成\n", + "任务 000621.SZ 完成\n", "任务 000653.SZ 完成\n", "任务 000658.SZ 完成\n", "任务 000660.SZ 完成\n", @@ -5544,8 +5591,8 @@ "任务 000765.SZ 完成\n", "任务 000769.SZ 完成\n", "任务 000780.SZ 完成\n", - "任务 000805.SZ 完成\n", "任务 000787.SZ 完成\n", + "任务 000805.SZ 完成\n", "任务 000806.SZ 完成\n", "任务 000817.SZ 完成\n", "任务 000827.SZ 完成\n", @@ -5681,15 +5728,15 @@ "任务 600270.SH 完成\n", "任务 600275.SH 完成\n", "任务 600277.SH 完成\n", - "任务 600286.SH 完成\n", "任务 600290.SH 完成\n", + "任务 600286.SH 完成\n", "任务 600291.SH 完成\n", "任务 600296.SH 完成\n", "任务 600297.SH 完成\n", "任务 600306.SH 完成\n", "任务 600311.SH 完成\n", - "任务 600321.SH 完成\n", "任务 600317.SH 完成\n", + "任务 600321.SH 完成\n", "任务 600357.SH 完成\n", "任务 600385.SH 完成\n", "任务 600393.SH 完成\n", @@ -5700,10 +5747,10 @@ "任务 600485.SH 完成\n", "任务 600532.SH 完成\n", "任务 600553.SH 完成\n", - "任务 600565.SH 完成\n", "任务 600555.SH 完成\n", - "任务 600607.SH 完成\n", + "任务 600565.SH 完成\n", "任务 600591.SH 完成\n", + "任务 600607.SH 完成\n", "任务 600614.SH 完成\n", "任务 600625.SH 完成\n", "任务 600627.SH 完成\n", @@ -5722,8 +5769,8 @@ "任务 600680.SH 完成\n", "任务 600687.SH 完成\n", "任务 600695.SH 完成\n", - "任务 600701.SH 完成\n", "任务 600700.SH 完成\n", + "任务 600701.SH 完成\n", "任务 600709.SH 完成\n", "任务 600723.SH 完成\n", "任务 600747.SH 完成\n", @@ -5748,12 +5795,12 @@ "任务 600870.SH 完成\n", "任务 600878.SH 完成\n", "任务 600890.SH 完成\n", - "任务 600896.SH 完成\n", "任务 600891.SH 完成\n", + "任务 600896.SH 完成\n", "任务 600899.SH 完成\n", "任务 600978.SH 完成\n", - "任务 601258.SH 完成\n", "任务 600991.SH 完成\n", + "任务 601258.SH 完成\n", "任务 601268.SH 完成\n", "任务 601299.SH 完成\n", "任务 601558.SH 完成\n", @@ -5766,127 +5813,72 @@ "任务 688555.SH 完成\n", "任务 832317.BJ 完成\n", "任务 833874.BJ 完成\n", - "任务 T00018.SH 完成\n", - "任务 833994.BJ 完成\n" + "任务 833994.BJ 完成\n", + "任务 T00018.SH 完成\n" ] } ], - "source": [ - "from concurrent.futures import ThreadPoolExecutor, as_completed\n", - "\n", - "# 读取本地保存的股票列表 CSV 文件(假设文件名为 stocks_data.csv)\n", - "stocks_df = pd.read_csv('../../../stocks_list.csv', encoding='utf-8-sig')\n", - "\n", - "# 用于存放所有股票的日线数据(每次获取的 DataFrame)\n", - "daily_data_list = []\n", - "\n", - "# API 调用计数和时间控制变量\n", - "api_call_count = 0\n", - "batch_start_time = time.time()\n", - "\n", - "\n", - "def get_data(ts_code):\n", - " daily_data = ts.pro_bar(ts_code=ts_code, adj='hfq', start_date=start_date)\n", - " # 如果返回数据不为空,则添加一列标识股票代码\n", - " if daily_data is not None and not daily_data.empty:\n", - " return daily_data\n", - "\n", - "\n", - "# 循环遍历每个股票代码并获取数据\n", - "with ThreadPoolExecutor(max_workers=2) as executor:\n", - " future_to_date = {executor.submit(get_data, row['ts_code']): row['ts_code'] for idx, row in stocks_df.iterrows()}\n", - "\n", - " for future in as_completed(future_to_date):\n", - " ts_code = future_to_date[future]\n", - " try:\n", - " result = future.result()\n", - " daily_data_list.append(result)\n", - " print(f\"任务 {ts_code} 完成\")\n", - " except Exception as e:\n", - " print(f\"获取 {ts_code} 数据时出错: {e}\")\n", - " # 计数一次 API 调用\n", - " api_call_count += 1\n", - "\n", - " # 每调用300次,检查时间是否少于1分钟,如果少于则等待剩余时间\n", - " if api_call_count % 300 == 0:\n", - " elapsed = time.time() - batch_start_time\n", - " if elapsed < 60:\n", - " sleep_time = 60 - elapsed\n", - " print(f\"已调用300次API,等待 {sleep_time:.2f} 秒以满足速率限制...\")\n", - " time.sleep(sleep_time)\n", - " # 重置批次起始时间\n", - " batch_start_time = time.time()\n" - ] + "execution_count": 3 }, { "cell_type": "code", - "execution_count": 4, "id": "35d605a189a83c1f", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:59:16.323785Z", - "start_time": "2025-02-11T15:59:16.059480Z" + "end_time": "2025-03-30T16:56:34.379208Z", + "start_time": "2025-03-30T16:56:34.194725Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " ts_code trade_date open high low close pre_close \\\n", - "0 000002.SZ 20250212 1317.35 1446.36 1295.55 1446.36 1315.54 \n", - "1 000001.SZ 20250212 1458.02 1460.57 1449.07 1459.29 1459.29 \n", - "2 000006.SZ 20250212 280.56 286.13 274.60 286.13 279.77 \n", - "3 000004.SZ 20250212 55.55 57.63 54.66 56.90 55.68 \n", - "4 000007.SZ 20250212 55.75 56.00 54.59 55.67 55.75 \n", - "... ... ... ... ... ... ... ... \n", - "5380 920111.BJ 20250212 26.91 28.31 26.58 27.68 26.55 \n", - "5381 920116.BJ 20250212 60.50 64.80 59.52 61.88 61.28 \n", - "5382 920118.BJ 20250212 26.55 26.90 26.00 26.51 26.78 \n", - "5383 920128.BJ 20250212 30.05 30.92 29.38 30.65 29.99 \n", - "5384 689009.SH 20250212 50.60 52.02 50.06 51.72 50.61 \n", - "\n", - " change pct_chg vol amount \n", - "0 130.82 9.94 3797389.87 2862874.540 \n", - "1 0.00 0.00 988806.83 1125323.633 \n", - "2 6.36 2.27 343298.87 243148.620 \n", - "3 1.22 2.19 183709.53 256068.396 \n", - "4 -0.08 -0.14 57191.80 38186.853 \n", - "... ... ... ... ... \n", - "5380 1.13 4.26 64642.66 178828.371 \n", - "5381 0.60 0.98 58150.24 360649.436 \n", - "5382 -0.27 -1.01 6872.99 18152.248 \n", - "5383 0.66 2.20 19876.68 60158.760 \n", - "5384 1.11 2.19 88778.51 452364.628 \n", - "\n", - "[5385 rows x 11 columns]\n" - ] - } - ], "source": [ "all_daily_data = pd.concat(daily_data_list, ignore_index=True)\n", "print(all_daily_data)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "446aeba1a846b34", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-11T15:59:27.088445Z", - "start_time": "2025-02-11T15:59:27.059335Z" - } - }, + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "所有日线数据已保存到 daily_data.h5\n" + " ts_code trade_date open high low close pre_close \\\n", + "0 000002.SZ 20250328 1302.82 1310.09 1291.92 1293.73 1302.82 \n", + "1 000002.SZ 20250327 1306.45 1311.90 1291.92 1302.82 1308.27 \n", + "2 000002.SZ 20250326 1306.45 1319.17 1304.63 1308.27 1306.45 \n", + "3 000002.SZ 20250325 1302.82 1313.72 1297.37 1306.45 1304.63 \n", + "4 000002.SZ 20250324 1335.52 1335.52 1299.18 1304.63 1337.34 \n", + "... ... ... ... ... ... ... ... \n", + "26882 689009.SH 20250328 65.05 65.96 64.68 65.61 65.76 \n", + "26883 689009.SH 20250327 62.39 66.02 62.20 65.76 62.43 \n", + "26884 689009.SH 20250326 62.94 62.94 61.95 62.43 62.44 \n", + "26885 689009.SH 20250325 63.33 63.44 61.87 62.44 63.04 \n", + "26886 689009.SH 20250324 62.06 64.05 61.63 63.04 62.13 \n", + "\n", + " change pct_chg vol amount \n", + "0 -9.09 -0.70 491441.40 351056.751 \n", + "1 -5.45 -0.42 562048.15 402664.947 \n", + "2 1.82 0.14 709377.47 511845.528 \n", + "3 1.82 0.14 655037.31 470444.410 \n", + "4 -32.71 -2.45 1050612.50 759032.562 \n", + "... ... ... ... ... \n", + "26882 -0.15 -0.23 57387.12 372490.242 \n", + "26883 3.33 5.33 108086.41 697534.473 \n", + "26884 -0.01 -0.02 45707.65 283488.125 \n", + "26885 -0.60 -0.95 47613.79 296056.671 \n", + "26886 0.91 1.46 101119.65 636305.468 \n", + "\n", + "[26887 rows x 11 columns]\n" ] } ], + "execution_count": 4 + }, + { + "cell_type": "code", + "id": "446aeba1a846b34", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-30T16:56:34.449212Z", + "start_time": "2025-03-30T16:56:34.379208Z" + } + }, "source": [ "\n", "# 合并所有获取到的日线数据\n", @@ -5896,7 +5888,17 @@ " print(\"所有日线数据已保存到 daily_data.h5\")\n", "else:\n", " print(\"未获取到任何日线数据。\")" - ] + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "所有日线数据已保存到 daily_data.h5\n" + ] + } + ], + "execution_count": 5 } ], "metadata": { @@ -5915,7 +5917,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.19" + "version": "3.11.11" } }, "nbformat": 4, diff --git a/code/data/update/update_money_flow.ipynb b/code/data/update/update_money_flow.ipynb index c7f63a3..7c5ea99 100644 --- a/code/data/update/update_money_flow.ipynb +++ b/code/data/update/update_money_flow.ipynb @@ -2,52 +2,31 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, "id": "b94bb1f2-5332-485e-ae1b-eea01f938106", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:21:54.821950Z", - "start_time": "2025-02-11T15:21:54.050569Z" + "end_time": "2025-03-30T16:42:37.847407Z", + "start_time": "2025-03-30T16:42:36.773187Z" } }, - "outputs": [], "source": [ "import tushare as ts\n", "\n", "ts.set_token('3a0741c702ee7e5e5f2bf1f0846bafaafe4e320833240b2a7e4a685f')\n", "pro = ts.pro_api()" - ] + ], + "outputs": [], + "execution_count": 1 }, { "cell_type": "code", - "execution_count": 2, "id": "742c29d453b9bb38", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:22:32.726905Z", - "start_time": "2025-02-11T15:22:25.018135Z" + "end_time": "2025-03-30T16:42:59.016187Z", + "start_time": "2025-03-30T16:42:37.850022Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Index: 8153941 entries, 0 to 5120\n", - "Data columns (total 2 columns):\n", - " # Column Dtype \n", - "--- ------ ----- \n", - " 0 ts_code object\n", - " 1 trade_date object\n", - "dtypes: object(2)\n", - "memory usage: 186.6+ MB\n", - "None\n", - "20250211\n", - "start_date: 20250212\n" - ] - } - ], "source": [ "import pandas as pd\n", "import time\n", @@ -61,39 +40,44 @@ " max_date = df['trade_date'].max()\n", "\n", "print(max_date)\n", - "trade_cal = pro.trade_cal(exchange='', start_date='20170101', end_date='20250220')\n", + "trade_cal = pro.trade_cal(exchange='', start_date='20170101', end_date='20250420')\n", "trade_cal = trade_cal[trade_cal['is_open'] == 1] # 只保留交易日\n", "trade_dates = trade_cal[trade_cal['cal_date'] > max_date]['cal_date'].tolist()\n", "start_date = min(trade_dates)\n", "print(f'start_date: {start_date}')" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "679ce40e-8d62-4887-970c-e1d8cbdeee6b", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-11T15:22:14.513527Z", - "start_time": "2025-02-11T15:22:12.973331Z" - }, - "scrolled": true - }, + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "任务 20250220 完成\n", - "任务 20250219 完成\n", - "任务 20250217 完成\n", - "任务 20250218 完成\n", - "任务 20250213 完成\n", - "任务 20250214 完成\n", - "任务 20250212 完成\n" + "\n", + "Index: 8297316 entries, 0 to 30724\n", + "Data columns (total 2 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object\n", + " 1 trade_date object\n", + "dtypes: object(2)\n", + "memory usage: 189.9+ MB\n", + "None\n", + "20250321\n", + "start_date: 20250324\n" ] } ], + "execution_count": 2 + }, + { + "cell_type": "code", + "id": "679ce40e-8d62-4887-970c-e1d8cbdeee6b", + "metadata": { + "scrolled": true, + "ExecuteTime": { + "end_time": "2025-03-30T16:43:03.168764Z", + "start_time": "2025-03-30T16:42:59.422934Z" + } + }, "source": [ "from concurrent.futures import ThreadPoolExecutor, as_completed\n", "\n", @@ -123,33 +107,69 @@ " except Exception as e:\n", " print(f\"获取 {trade_date} 数据时出错: {e}\")\n", "\n" - ] + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "任务 20250418 完成\n", + "任务 20250417 完成\n", + "任务 20250415 完成\n", + "任务 20250416 完成\n", + "任务 20250414 完成\n", + "任务 20250411 完成\n", + "任务 20250410 完成\n", + "任务 20250409 完成\n", + "任务 20250408 完成\n", + "任务 20250407 完成\n", + "任务 20250403 完成\n", + "任务 20250402 完成\n", + "任务 20250401 完成\n", + "任务 20250331 完成\n", + "任务 20250328 完成\n", + "任务 20250327 完成\n", + "任务 20250326 完成\n", + "任务 20250325 完成\n", + "任务 20250324 完成\n" + ] + } + ], + "execution_count": 3 }, { "cell_type": "code", - "execution_count": 4, "id": "9af80516849d4e80", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:22:16.656650Z", - "start_time": "2025-02-11T15:22:16.639271Z" + "end_time": "2025-03-30T16:43:03.181032Z", + "start_time": "2025-03-30T16:43:03.173867Z" } }, - "outputs": [], "source": [ "all_daily_data_df = pd.concat(all_daily_data, ignore_index=True)\n" - ] + ], + "outputs": [], + "execution_count": 4 }, { "cell_type": "code", - "execution_count": 5, "id": "a2b05187-437f-4053-bc43-bd80d4cf8b0e", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:22:20.447350Z", - "start_time": "2025-02-11T15:22:19.145561Z" + "end_time": "2025-03-30T16:43:05.401668Z", + "start_time": "2025-03-30T16:43:03.197033Z" } }, + "source": [ + "\n", + "# 将所有数据合并为一个 DataFrame\n", + "\n", + "# 将数据保存为 HDF5 文件(table 格式)\n", + "all_daily_data_df.to_hdf(h5_filename, key='money_flow', mode='a', format='table', append=True, data_columns=True)\n", + "\n", + "print(\"所有每日基础数据获取并保存完毕!\")" + ], "outputs": [ { "name": "stdout", @@ -159,15 +179,7 @@ ] } ], - "source": [ - "\n", - "# 将所有数据合并为一个 DataFrame\n", - "\n", - "# 将数据保存为 HDF5 文件(table 格式)\n", - "all_daily_data_df.to_hdf(h5_filename, key='money_flow', mode='a', format='table', append=True, data_columns=True)\n", - "\n", - "print(\"所有每日基础数据获取并保存完毕!\")" - ] + "execution_count": 5 } ], "metadata": { @@ -186,7 +198,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.19" + "version": "3.11.11" } }, "nbformat": 4, diff --git a/code/data/update/update_stk_limit.ipynb b/code/data/update/update_stk_limit.ipynb index edd3a90..7dc3221 100644 --- a/code/data/update/update_stk_limit.ipynb +++ b/code/data/update/update_stk_limit.ipynb @@ -2,58 +2,31 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, "id": "500802dc-7a20-48b7-a470-a4bae3ec534b", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:18:36.892437Z", - "start_time": "2025-02-11T15:18:36.020822Z" + "end_time": "2025-03-30T16:42:39.056767Z", + "start_time": "2025-03-30T16:42:37.817887Z" } }, - "outputs": [], "source": [ "import tushare as ts\n", "\n", "ts.set_token('3a0741c702ee7e5e5f2bf1f0846bafaafe4e320833240b2a7e4a685f')\n", "pro = ts.pro_api()" - ] + ], + "outputs": [], + "execution_count": 1 }, { "cell_type": "code", - "execution_count": 2, "id": "5a84bc9da6d54868", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:20:12.573607Z", - "start_time": "2025-02-11T15:20:00.110127Z" + "end_time": "2025-03-30T16:42:59.784780Z", + "start_time": "2025-03-30T16:42:39.056767Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " ts_code trade_date\n", - "4682 600310.SH 20250211\n", - "4683 600312.SH 20250211\n", - "4684 600313.SH 20250211\n", - "4673 600299.SH 20250211\n", - "0 000001.SZ 20250211\n", - "\n", - "Index: 10040878 entries, 0 to 10040877\n", - "Data columns (total 2 columns):\n", - " # Column Dtype \n", - "--- ------ ----- \n", - " 0 ts_code object\n", - " 1 trade_date object\n", - "dtypes: object(2)\n", - "memory usage: 229.8+ MB\n", - "None\n", - "20250211\n", - "20250212\n" - ] - } - ], "source": [ "import pandas as pd\n", "import time\n", @@ -68,39 +41,50 @@ " max_date = df['trade_date'].max()\n", "\n", "print(max_date)\n", - "trade_cal = pro.trade_cal(exchange='', start_date='20170101', end_date='20250220')\n", + "trade_cal = pro.trade_cal(exchange='', start_date='20170101', end_date='20250420')\n", "trade_cal = trade_cal[trade_cal['is_open'] == 1] # 只保留交易日\n", "trade_dates = trade_cal[trade_cal['cal_date'] > max_date]['cal_date'].tolist()\n", "start_date = min(trade_dates)\n", "print(start_date)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "bb3191de-27a2-4c89-a3b5-32a0d7b9496f", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-11T15:21:27.831699Z", - "start_time": "2025-02-11T15:21:26.665039Z" - }, - "scrolled": true - }, + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "任务 20250219 完成\n", - "任务 20250220 完成\n", - "任务 20250217 完成\n", - "任务 20250218 完成\n", - "任务 20250214 完成\n", - "任务 20250213 完成\n", - "任务 20250212 完成\n" + " ts_code trade_date\n", + "4705 600289.SH 20250321\n", + "4706 600292.SH 20250321\n", + "4707 600293.SH 20250321\n", + "4696 600279.SH 20250321\n", + "7051 920116.BJ 20250321\n", + "\n", + "Index: 10237887 entries, 0 to 35266\n", + "Data columns (total 2 columns):\n", + " # Column Dtype \n", + "--- ------ ----- \n", + " 0 ts_code object\n", + " 1 trade_date object\n", + "dtypes: object(2)\n", + "memory usage: 234.3+ MB\n", + "None\n", + "20250321\n", + "20250324\n" ] } ], + "execution_count": 2 + }, + { + "cell_type": "code", + "id": "bb3191de-27a2-4c89-a3b5-32a0d7b9496f", + "metadata": { + "scrolled": true, + "ExecuteTime": { + "end_time": "2025-03-30T16:43:03.372001Z", + "start_time": "2025-03-30T16:43:00.012140Z" + } + }, "source": [ "from concurrent.futures import ThreadPoolExecutor, as_completed\n", "\n", @@ -131,69 +115,143 @@ " except Exception as e:\n", " print(f\"获取 {trade_date} 数据时出错: {e}\")\n", "\n" - ] + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "任务 20250418 完成\n", + "任务 20250417 完成\n", + "任务 20250416 完成\n", + "任务 20250415 完成\n", + "任务 20250411 完成\n", + "任务 20250414 完成\n", + "任务 20250409 完成\n", + "任务 20250410 完成\n", + "任务 20250408 完成\n", + "任务 20250407 完成\n", + "任务 20250403 完成\n", + "任务 20250402 完成\n", + "任务 20250401 完成\n", + "任务 20250331 完成\n", + "任务 20250327 完成\n", + "任务 20250328 完成\n", + "任务 20250326 完成\n", + "任务 20250325 完成\n", + "任务 20250324 完成\n" + ] + } + ], + "execution_count": 3 }, { "cell_type": "code", - "execution_count": 4, "id": "96a81aa5890ea3c3", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:21:29.294283Z", - "start_time": "2025-02-11T15:21:29.247112Z" + "end_time": "2025-03-30T16:43:03.397757Z", + "start_time": "2025-03-30T16:43:03.384786Z" } }, + "source": [ + "print(all_daily_data)\n", + "# 将所有数据合并为一个 DataFrame\n", + "all_daily_data_df = pd.concat(all_daily_data, ignore_index=True)" + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ trade_date ts_code up_limit down_limit\n", - "0 20250213 000001.SZ 12.56 10.28\n", - "1 20250213 000002.SZ 8.76 7.16\n", - "2 20250213 000004.SZ 15.40 12.60\n", - "3 20250213 000006.SZ 7.92 6.48\n", - "4 20250213 000007.SZ 7.39 6.05\n", + "0 20250327 000001.SZ 12.52 10.24\n", + "1 20250327 000002.SZ 7.92 6.48\n", + "2 20250327 000004.SZ 11.40 9.32\n", + "3 20250327 000006.SZ 7.44 6.08\n", + "4 20250327 000007.SZ 7.00 5.72\n", "... ... ... ... ...\n", - "7014 20250213 920108.BJ 27.22 14.66\n", - "7015 20250213 920111.BJ 35.98 19.38\n", - "7016 20250213 920116.BJ 80.44 43.32\n", - "7017 20250213 920118.BJ 34.46 18.56\n", - "7018 20250213 920128.BJ 39.84 21.46\n", + "7059 20250327 920108.BJ 33.56 18.08\n", + "7060 20250327 920111.BJ 40.57 21.85\n", + "7061 20250327 920116.BJ 126.29 68.01\n", + "7062 20250327 920118.BJ 44.14 23.78\n", + "7063 20250327 920128.BJ 47.35 25.51\n", "\n", - "[7019 rows x 4 columns], trade_date ts_code up_limit down_limit\n", - "0 20250212 000001.SZ 12.56 10.28\n", - "1 20250212 000002.SZ 7.96 6.52\n", - "2 20250212 000004.SZ 15.07 12.33\n", - "3 20250212 000006.SZ 7.74 6.34\n", - "4 20250212 000007.SZ 7.40 6.06\n", + "[7064 rows x 4 columns], trade_date ts_code up_limit down_limit\n", + "0 20250328 000001.SZ 12.53 10.25\n", + "1 20250328 000002.SZ 7.89 6.45\n", + "2 20250328 000004.SZ 11.19 9.15\n", + "3 20250328 000006.SZ 8.18 6.70\n", + "4 20250328 000007.SZ 6.99 5.72\n", "... ... ... ... ...\n", - "7014 20250212 920108.BJ 27.41 14.77\n", - "7015 20250212 920111.BJ 34.51 18.59\n", - "7016 20250212 920116.BJ 79.66 42.90\n", - "7017 20250212 920118.BJ 34.81 18.75\n", - "7018 20250212 920128.BJ 38.98 21.00\n", + "7060 20250328 920108.BJ 31.03 16.71\n", + "7061 20250328 920111.BJ 39.65 21.35\n", + "7062 20250328 920116.BJ 115.67 62.29\n", + "7063 20250328 920118.BJ 41.00 22.08\n", + "7064 20250328 920128.BJ 44.83 24.15\n", "\n", - "[7019 rows x 4 columns]]\n" + "[7065 rows x 4 columns], trade_date ts_code up_limit down_limit\n", + "0 20250326 000001.SZ 12.57 10.29\n", + "1 20250326 000002.SZ 7.91 6.47\n", + "2 20250326 000004.SZ 11.28 9.23\n", + "3 20250326 000006.SZ 7.17 5.87\n", + "4 20250326 000007.SZ 6.67 5.45\n", + "... ... ... ... ...\n", + "7056 20250326 920108.BJ 33.96 18.30\n", + "7057 20250326 920111.BJ 41.92 22.58\n", + "7058 20250326 920116.BJ 133.64 71.96\n", + "7059 20250326 920118.BJ 41.93 22.59\n", + "7060 20250326 920128.BJ 49.40 26.60\n", + "\n", + "[7061 rows x 4 columns], trade_date ts_code up_limit down_limit\n", + "0 20250325 000001.SZ 12.52 10.24\n", + "1 20250325 000002.SZ 7.90 6.46\n", + "2 20250325 000004.SZ 11.55 9.45\n", + "3 20250325 000006.SZ 7.13 5.83\n", + "4 20250325 000007.SZ 6.60 5.40\n", + "... ... ... ... ...\n", + "7055 20250325 920108.BJ 33.30 17.94\n", + "7056 20250325 920111.BJ 39.97 21.53\n", + "7057 20250325 920116.BJ 137.78 74.20\n", + "7058 20250325 920118.BJ 39.52 21.28\n", + "7059 20250325 920128.BJ 46.22 24.90\n", + "\n", + "[7060 rows x 4 columns], trade_date ts_code up_limit down_limit\n", + "0 20250324 000001.SZ 12.56 10.28\n", + "1 20250324 000002.SZ 8.10 6.62\n", + "2 20250324 000004.SZ 12.82 10.49\n", + "3 20250324 000006.SZ 7.44 6.08\n", + "4 20250324 000007.SZ 6.89 5.63\n", + "... ... ... ... ...\n", + "7053 20250324 920108.BJ 34.84 18.76\n", + "7054 20250324 920111.BJ 40.41 21.77\n", + "7055 20250324 920116.BJ 134.55 72.45\n", + "7056 20250324 920118.BJ 38.67 20.83\n", + "7057 20250324 920128.BJ 45.86 24.70\n", + "\n", + "[7058 rows x 4 columns]]\n" ] } ], - "source": [ - "print(all_daily_data)\n", - "# 将所有数据合并为一个 DataFrame\n", - "all_daily_data_df = pd.concat(all_daily_data, ignore_index=True)" - ] + "execution_count": 4 }, { "cell_type": "code", - "execution_count": 5, "id": "ad9733a1-2f42-43ee-a98c-0bf699304c21", "metadata": { "ExecuteTime": { - "end_time": "2025-02-11T15:20:37.999493Z", - "start_time": "2025-02-11T15:20:37.375220Z" + "end_time": "2025-03-30T16:43:03.696614Z", + "start_time": "2025-03-30T16:43:03.411036Z" } }, + "source": [ + "\n", + "\n", + "# 将数据保存为 HDF5 文件(table 格式)\n", + "all_daily_data_df.to_hdf(h5_filename, key='stk_limit', mode='a', format='table', append=True, data_columns=True)\n", + "\n", + "print(\"所有每日基础数据获取并保存完毕!\")" + ], "outputs": [ { "name": "stdout", @@ -203,22 +261,20 @@ ] } ], - "source": [ - "\n", - "\n", - "# 将数据保存为 HDF5 文件(table 格式)\n", - "all_daily_data_df.to_hdf(h5_filename, key='stk_limit', mode='a', format='table', append=True, data_columns=True)\n", - "\n", - "print(\"所有每日基础数据获取并保存完毕!\")" - ] + "execution_count": 5 }, { "cell_type": "code", - "execution_count": null, "id": "7e777f1f-4d54-4a74-b916-691ede6af055", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-30T16:43:03.713628Z", + "start_time": "2025-03-30T16:43:03.711521Z" + } + }, + "source": [], "outputs": [], - "source": [] + "execution_count": null } ], "metadata": { @@ -237,7 +293,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.19" + "version": "3.11.11" } }, "nbformat": 4, diff --git a/code/train/MultiClassify.ipynb b/code/train/MultiClassify.ipynb new file mode 100644 index 0000000..0ab875a --- /dev/null +++ b/code/train/MultiClassify.ipynb @@ -0,0 +1,1158 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "79a7758178bafdd3", + "metadata": { + "jupyter": { + "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-03T13:08:58.678627Z", + "start_time": "2025-03-03T13:08:58.674698Z" + } + }, + "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": 3 + }, + { + "cell_type": "code", + "id": "a79cafb06a7e0e43", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:09:40.479598Z", + "start_time": "2025-03-03T13:08:58.750948Z" + } + }, + "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'],\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(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", + "\n", + "RangeIndex: 8369855 entries, 0 to 8369854\n", + "Data columns (total 21 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 turnover_rate float64 \n", + " 8 pe_ttm float64 \n", + " 9 circ_mv float64 \n", + " 10 volume_ratio float64 \n", + " 11 is_st bool \n", + " 12 up_limit float64 \n", + " 13 down_limit float64 \n", + " 14 buy_sm_vol float64 \n", + " 15 sell_sm_vol float64 \n", + " 16 buy_lg_vol float64 \n", + " 17 sell_lg_vol float64 \n", + " 18 buy_elg_vol float64 \n", + " 19 sell_elg_vol float64 \n", + " 20 net_mf_vol float64 \n", + "dtypes: bool(1), datetime64[ns](1), float64(18), object(1)\n", + "memory usage: 1.3+ GB\n", + "None\n" + ] + } + ], + "execution_count": 4 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:09:42.969235Z", + "start_time": "2025-03-03T13:09:40.522863Z" + } + }, + "cell_type": "code", + "source": [ + "print('industry')\n", + "df = read_and_merge_h5_data('../../data/industry_data.h5', key='industry_data',\n", + " columns=['ts_code', 'l2_code'],\n", + " df=df, on=['ts_code'], join='left')\n" + ], + "id": "38879273d3574ae3", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "industry\n", + "left merge on ['ts_code']\n" + ] + } + ], + "execution_count": 5 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:09:43.066598Z", + "start_time": "2025-03-03T13:09:42.976241Z" + } + }, + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "\n", + "def calculate_indicators(df):\n", + " \"\"\"\n", + " 计算四个指标:当日涨跌幅、5日移动平均、RSI、MACD。\n", + " \"\"\"\n", + " df = df.sort_values('trade_date')\n", + " df['daily_return'] = (df['close'] - df['pre_close']) / df['pre_close'] * 100\n", + " # df['5_day_ma'] = df['close'].rolling(window=5).mean()\n", + " delta = df['close'].diff()\n", + " gain = delta.where(delta > 0, 0)\n", + " loss = -delta.where(delta < 0, 0)\n", + " avg_gain = gain.rolling(window=14).mean()\n", + " avg_loss = loss.rolling(window=14).mean()\n", + " rs = avg_gain / avg_loss\n", + " df['RSI'] = 100 - (100 / (1 + rs))\n", + "\n", + " # 计算MACD\n", + " ema12 = df['close'].ewm(span=12, adjust=False).mean()\n", + " ema26 = df['close'].ewm(span=26, adjust=False).mean()\n", + " df['MACD'] = ema12 - ema26\n", + " df['Signal_line'] = df['MACD'].ewm(span=9, adjust=False).mean()\n", + " df['MACD_hist'] = df['MACD'] - df['Signal_line']\n", + "\n", + " 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', 'MACD_hist'],\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" + ], + "id": "a4eec8c93f6a7cc3", + "outputs": [], + "execution_count": 6 + }, + { + "cell_type": "code", + "id": "c4e9e1d31da6dba6", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:09:43.133133Z", + "start_time": "2025-03-03T13:09:43.090288Z" + } + }, + "source": [ + "import numpy as np\n", + "import talib\n", + "\n", + "\n", + "def get_technical_factor(df):\n", + " # 按股票和日期排序\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code', group_keys=False)\n", + "\n", + " # 计算 up 和 down\n", + " df['log_close'] = np.log(df['close'])\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", + " # 计算 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", + " df['obv-maobv_6'] = df['obv'] - df['maobv_6']\n", + "\n", + " # 计算 RSI\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", + " # 计算 avg_close_5\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", + " # 计算比值指标\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", + " return df\n", + "\n", + "\n", + "def get_act_factor(df, cat=True):\n", + " # 按股票和日期排序\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code', group_keys=False)\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", + " if cat:\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", + " # 根据 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", + " return df\n", + "\n", + "\n", + "def get_money_flow_factor(df):\n", + " # 计算资金流相关因子(字段名称见 tushare 数据说明)\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", + " return df\n", + "\n", + "\n", + "def get_alpha_factor(df):\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code')\n", + "\n", + " # alpha_022: 当前 close 与 5 日前 close 差值\n", + " df['alpha_022'] = grouped['close'].transform(lambda x: x - x.shift(5))\n", + "\n", + " # alpha_003: (close - open) / (high - low)\n", + " df['alpha_003'] = np.where(df['high'] != df['low'],\n", + " (df['close'] - df['open']) / (df['high'] - df['low']),\n", + " 0)\n", + "\n", + " # alpha_007: 计算过去5日 close 与 vol 的相关性,并按 trade_date 排名\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", + " # alpha_013: 计算过去5日 close 之和 - 20日 close 之和,并按 trade_date 排名\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", + " return df\n", + "\n" + ], + "outputs": [], + "execution_count": 7 + }, + { + "cell_type": "code", + "id": "53f86ddc0677a6d7", + "metadata": { + "scrolled": true, + "ExecuteTime": { + "end_time": "2025-03-03T13:09:50.856330Z", + "start_time": "2025-03-03T13:09:43.137146Z" + } + }, + "source": [ + "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.median())\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 = 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": 8 + }, + { + "cell_type": "code", + "id": "dbe2fd8021b9417f", + "metadata": { + "jupyter": { + "source_hidden": true + }, + "scrolled": true, + "ExecuteTime": { + "end_time": "2025-03-03T13:09:50.873842Z", + "start_time": "2025-03-03T13:09:50.870793Z" + } + }, + "source": [ + "origin_columns = df.columns.tolist()\n", + "origin_columns = [col for col in origin_columns if col not in ['turnover_rate', 'pe_ttm', 'volume_ratio', 'l2_code']]\n", + "origin_columns = [col for col in origin_columns if col not in index_data.columns]\n" + ], + "outputs": [], + "execution_count": 9 + }, + { + "cell_type": "code", + "id": "5f3d9aece75318cd", + "metadata": { + "jupyter": { + "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-03T13:11:03.268415Z", + "start_time": "2025-03-03T13:09:50.890356Z" + } + }, + "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.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 = 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: 5732462 entries, 1964 to 5732460\n", + "Data columns (total 72 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 turnover_rate float64 \n", + " 8 pe_ttm float64 \n", + " 9 circ_mv float64 \n", + " 10 volume_ratio float64 \n", + " 11 is_st bool \n", + " 12 up_limit float64 \n", + " 13 down_limit float64 \n", + " 14 buy_sm_vol float64 \n", + " 15 sell_sm_vol float64 \n", + " 16 buy_lg_vol float64 \n", + " 17 sell_lg_vol float64 \n", + " 18 buy_elg_vol float64 \n", + " 19 sell_elg_vol float64 \n", + " 20 net_mf_vol float64 \n", + " 21 cat_l2_code object \n", + " 22 log_close float64 \n", + " 23 up float64 \n", + " 24 down float64 \n", + " 25 atr_14 float64 \n", + " 26 atr_6 float64 \n", + " 27 obv float64 \n", + " 28 maobv_6 float64 \n", + " 29 obv-maobv_6 float64 \n", + " 30 rsi_3 float64 \n", + " 31 rsi_6 float64 \n", + " 32 rsi_9 float64 \n", + " 33 return_5 float64 \n", + " 34 return_10 float64 \n", + " 35 return_20 float64 \n", + " 36 avg_close_5 float64 \n", + " 37 std_return_5 float64 \n", + " 38 std_return_15 float64 \n", + " 39 std_return_25 float64 \n", + " 40 std_return_90 float64 \n", + " 41 std_return_90_2 float64 \n", + " 42 std_return_5 / std_return_90 float64 \n", + " 43 std_return_5 / std_return_25 float64 \n", + " 44 std_return_90 - std_return_90_2 float64 \n", + " 45 ema_5 float64 \n", + " 46 ema_13 float64 \n", + " 47 ema_20 float64 \n", + " 48 ema_60 float64 \n", + " 49 act_factor1 float64 \n", + " 50 act_factor2 float64 \n", + " 51 act_factor3 float64 \n", + " 52 act_factor4 float64 \n", + " 53 cat_af1 bool \n", + " 54 cat_af2 bool \n", + " 55 cat_af3 bool \n", + " 56 cat_af4 bool \n", + " 57 act_factor5 float64 \n", + " 58 act_factor6 float64 \n", + " 59 rank_act_factor1 float64 \n", + " 60 rank_act_factor2 float64 \n", + " 61 rank_act_factor3 float64 \n", + " 62 active_buy_volume_large float64 \n", + " 63 active_buy_volume_big float64 \n", + " 64 active_buy_volume_small float64 \n", + " 65 buy_lg_vol_minus_sell_lg_vol float64 \n", + " 66 buy_elg_vol_minus_sell_elg_vol float64 \n", + " 67 log(circ_mv) float64 \n", + " 68 alpha_022 float64 \n", + " 69 alpha_003 float64 \n", + " 70 alpha_007 float64 \n", + " 71 alpha_013 float64 \n", + "dtypes: bool(5), datetime64[ns](1), float64(64), object(2)\n", + "memory usage: 2.9+ GB\n", + "None\n" + ] + } + ], + "execution_count": 10 + }, + { + "cell_type": "code", + "id": "f4f16d63ad18d1bc", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:11:03.654681Z", + "start_time": "2025-03-03T13:11:03.638505Z" + } + }, + "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", + " num_features = [col for col in feature_columns if 'cat' not in col and 'industry' not in col]\n", + "\n", + " # 遍历所有数值型特征\n", + " for feature in num_features:\n", + " if feature == 'trade_date': # 不需要对 'trade_date' 计算偏差\n", + " continue\n", + "\n", + " grouped_median = df.groupby(['trade_date', groupby_col])[feature].transform('median')\n", + " deviation_col_name = f'deviation_median_{feature}'\n", + " new_columns[deviation_col_name] = df[feature] - grouped_median\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": "0ebdfb92-d88b-4b5c-a715-675dab876fc0", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:11:03.763536Z", + "start_time": "2025-03-03T13:11:03.756900Z" + } + }, + "source": [ + "def get_qcuts(series, quantiles):\n", + " q = pd.qcut(series, q=quantiles, labels=False, duplicates='drop')\n", + " return q[-1] # 返回窗口最后一个元素的分位数标签\n", + "\n", + "\n", + "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_return'] = (df['future_close'] - df['close']) / df['close']\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", + " df['sharpe_ratio'] = df['future_return'] * df['future_volatility']\n", + " df['sharpe_ratio'].replace([np.inf, -np.inf], np.nan, inplace=True)\n", + "\n", + " return df['sharpe_ratio']\n", + "\n", + "\n" + ], + "outputs": [], + "execution_count": 12 + }, + { + "cell_type": "code", + "id": "fbb968383f8cf2c7", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:24:10.876647Z", + "start_time": "2025-03-03T13:22:30.406605Z" + } + }, + "source": [ + "days = 5\n", + "future_close = df.groupby('ts_code')['close'].shift(-days)\n", + "future_open = df.groupby('ts_code')['open'].shift(-1)\n", + "future_return = (future_close - future_open) / future_open\n", + "df['label'] = future_return\n", + "df['label'] = remove_outliers_label_percentile(df['label'])\n", + "df['label'] = df.groupby('trade_date')['label'].transform(\n", + " lambda x: pd.qcut(x, q=5, labels=False, duplicates='drop')\n", + ")\n", + "print(df['label'].unique())\n", + "\n", + "\n", + "# df['label'] = remove_outliers_label_percentile(df['label'])\n", + "\n", + "# df = df.apply(lambda x: x.astype('float32') if x.dtype in ['float64', 'float32'] else x)\n", + "df = df.sort_values(by=['trade_date', 'ts_code'])\n", + "train_data = df[(df['trade_date'] <= '2024-03-01') & (df['trade_date'] >= '2016-01-01')]\n", + "test_data = df[(df['trade_date'] >= '2024-03-01')]\n", + "\n", + "train_data = train_data.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nsmallest(3000, 'log(circ_mv)')\n", + ")\n", + "test_data = test_data.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nsmallest(3000, 'log(circ_mv)')\n", + ")\n", + "train_data = train_data.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nlargest(1000, 'return_20')\n", + ")\n", + "test_data = test_data.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nlargest(1000, 'return_20')\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 = [col for col in train_data.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 'score' 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(feature_columns)\n", + "\n", + "feature_columns_new = feature_columns[:]\n", + "train_data, feature_columns_new = create_deviation_within_dates(train_data, feature_columns)\n", + "print(f'feature_columns size: {len(feature_columns_new)}')\n", + "test_data, feature_columns_new = create_deviation_within_dates(test_data, feature_columns)\n", + "print(f'feature_columns size: {len(feature_columns_new)}')\n", + "\n", + "train_data = train_data.dropna(subset=feature_columns_new)\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_new)\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 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')" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Removed 131011 outliers.\n", + "[ 1. 0. 3. 2. 4. nan]\n", + "['turnover_rate', 'pe_ttm', 'volume_ratio', 'cat_l2_code', 'log_close', 'up', 'down', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'obv-maobv_6', 'rsi_3', 'rsi_6', 'rsi_9', 'return_5', 'return_10', 'return_20', 'avg_close_5', 'std_return_5', 'std_return_15', 'std_return_25', 'std_return_90', 'std_return_90_2', 'std_return_5 / std_return_90', 'std_return_5 / std_return_25', 'std_return_90 - std_return_90_2', 'ema_5', 'ema_13', 'ema_20', 'ema_60', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'cat_af1', 'cat_af2', 'cat_af3', 'cat_af4', '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', 'buy_lg_vol_minus_sell_lg_vol', 'buy_elg_vol_minus_sell_elg_vol', 'log(circ_mv)', 'alpha_022', 'alpha_003', 'alpha_007', 'alpha_013', '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_obv_deviation', 'industry_return_5_deviation', 'industry_return_20_deviation', 'industry_act_factor1_deviation', 'industry_act_factor2_deviation', 'industry_act_factor3_deviation', 'industry_act_factor4_deviation', 'industry_return_5_percentile', '000852.SH_MACD', '000905.SH_MACD', '399006.SZ_MACD', '000852.SH_MACD_hist', '000905.SH_MACD_hist', '399006.SZ_MACD_hist', '000852.SH_RSI', '000905.SH_RSI', '399006.SZ_RSI', '000852.SH_Signal_line', '000905.SH_Signal_line', '399006.SZ_Signal_line', '000852.SH_daily_return', '000905.SH_daily_return', '399006.SZ_daily_return']\n", + "feature_columns size: 221\n", + "feature_columns size: 221\n", + "1341891\n", + "最小日期: 2017-04-06\n", + "最大日期: 2024-03-01\n", + "181520\n", + "最小日期: 2024-03-01\n", + "最大日期: 2025-02-28\n" + ] + } + ], + "execution_count": 33 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:25:32.375790Z", + "start_time": "2025-03-03T13:24:11.128103Z" + } + }, + "cell_type": "code", + "source": [ + "def remove_highly_correlated_features(df, feature_columns, threshold=0.8):\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", + " return remaining_features\n", + "\n", + "\n", + "feature_columns_new = remove_highly_correlated_features(train_data, feature_columns_new)\n", + "keep_columns = [col for col in train_data.columns if\n", + " col in feature_columns_new or col in ['ts_code', 'trade_date', 'label']]\n", + "train_data = train_data[keep_columns]\n", + "test_data = test_data[keep_columns]\n", + "print(f'feature_columns size: {len(feature_columns_new)}')" + ], + "id": "26c4c873b83ecc38", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "feature_columns size: 83\n" + ] + } + ], + "execution_count": 34 + }, + { + "cell_type": "code", + "id": "8f134d435f71e9e2", + "metadata": { + "jupyter": { + "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-03T13:29:03.972545Z", + "start_time": "2025-03-03T13:29:03.965555Z" + } + }, + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "from catboost import Pool\n", + "import lightgbm as lgb\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\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", + " use_optuna=False):\n", + " train_data_df, test_data_df = train_data_df.dropna(subset=['label']), test_data_df.dropna(subset=['label'])\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", + " categorical_feature = [i for i, col in enumerate(feature_columns) if col.startswith('cat')]\n", + " print(f'categorical_feature: {categorical_feature}')\n", + " train_data = lgb.Dataset(X_train, label=y_train, categorical_feature=categorical_feature)\n", + " val_data = lgb.Dataset(X_val, label=y_val, categorical_feature=categorical_feature)\n", + " model = lgb.train(\n", + " params, train_data, num_boost_round=num_boost_round,\n", + " valid_sets=[train_data, val_data], valid_names=['train', 'valid'],\n", + " callbacks=callbacks\n", + " )\n", + "\n", + " if print_feature_importance:\n", + " lgb.plot_metric(evals)\n", + " # lgb.plot_tree(model, figsize=(20, 8))\n", + " lgb.plot_importance(model, importance_type='split', max_num_features=20)\n", + " plt.show()\n", + " return model\n", + "\n", + "\n", + "from catboost import CatBoostClassifier\n", + "import pandas as pd\n", + "\n", + "\n", + "def train_catboost(train_data_df, test_data_df, feature_columns, params=None, plot=False):\n", + " train_data_df, test_data_df = train_data_df.dropna(subset=['label']), test_data_df.dropna(subset=['label'])\n", + " # train_data_df = train_data_df[train_data_df['label'].isin([0, 2, 4])]\n", + " # test_data_df = test_data_df[test_data_df['label'].isin([0, 2, 4])]\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", + " cat_features = [i for i, col in enumerate(feature_columns) if col.startswith('cat')]\n", + " print(f'cat_features: {cat_features}')\n", + " train_pool = Pool(data=X_train, label=y_train, cat_features=cat_features)\n", + " val_pool = Pool(data=X_val, label=y_val, cat_features=cat_features)\n", + "\n", + " model = CatBoostClassifier(**params)\n", + " model.fit(train_pool,\n", + " eval_set=val_pool, plot=plot)\n", + "\n", + " return model, scaler" + ], + "outputs": [], + "execution_count": 44 + }, + { + "cell_type": "code", + "id": "4a4542e1ed6afe7d", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:29:03.995376Z", + "start_time": "2025-03-03T13:29:03.991323Z" + } + }, + "source": [ + "light_params = {\n", + " 'objective': 'binary',\n", + " 'metric': 'average_precision',\n", + " 'learning_rate': 0.05,\n", + " 'is_unbalance': True,\n", + " 'num_leaves': 2048,\n", + " 'min_data_in_leaf': 1024,\n", + " 'max_depth': 32,\n", + " 'max_bin': 1024,\n", + " 'feature_fraction': 0.7,\n", + " 'bagging_fraction': 0.7,\n", + " 'bagging_freq': 5,\n", + " # 'lambda_l1': 80,\n", + " # 'lambda_l2': 65,\n", + " 'verbosity': -1,\n", + " 'num_threads': 16\n", + "}" + ], + "outputs": [], + "execution_count": 45 + }, + { + "cell_type": "code", + "id": "beeb098799ecfa6a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:29:04.196597Z", + "start_time": "2025-03-03T13:29:04.190141Z" + } + }, + "source": [ + "print('train data size: ', len(train_data))\n", + "\n", + "evals = {}\n", + "# model, scaler = train_light_model(train_data, test_data, light_params, feature_columns_new,\n", + "# [lgb.log_evaluation(period=500),\n", + "# lgb.callback.record_evaluation(evals),\n", + "# lgb.early_stopping(50, first_metric_only=True)\n", + "# ], evals,\n", + "# num_boost_round=1000, use_optuna=False,\n", + "# print_feature_importance=True)" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train data size: 1341891\n" + ] + } + ], + "execution_count": 46 + }, + { + "cell_type": "code", + "id": "445dff84-70b2-4fc9-a9b6-1251993324d6", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:32:01.078551Z", + "start_time": "2025-03-03T13:29:04.236216Z" + } + }, + "source": [ + "catboost_params = {\n", + " 'loss_function': 'MultiClass', # 适用于多分类任务\n", + " 'eval_metric': 'AUC', # 评估指标,也可以用 'Accuracy' 或 'TotalF1'\n", + " 'iterations': 5000, # 最大迭代次数\n", + " 'learning_rate': 0.01, # 学习率\n", + " 'depth': 10, # 树的深度,控制模型复杂度\n", + " 'l2_leaf_reg': 10, # L2 正则化\n", + " 'verbose': 500, # 每 500 次迭代输出一次日志\n", + " 'early_stopping_rounds': 100, # 提前停止的轮数\n", + " 'task_type': 'GPU', # 使用 GPU 加速训练\n", + "}\n", + "\n", + "feature_weights = {col: 2.0 if col in ['act_factor', 'af'] else 1.0 for col in feature_columns_new}\n", + "catboost_params['feature_weights'] = feature_weights\n", + "\n", + "model, scaler = train_catboost(train_data, test_data, feature_columns_new, catboost_params, plot=True)" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cat_features: [3, 20, 21, 22, 23]\n" + ] + }, + { + "data": { + "text/plain": [ + "MetricVisualizer(layout=Layout(align_self='stretch', height='500px'))" + ], + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "7d14b6917a5a499ea87455ebd47a1138" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Default metric period is 5 because AUC is/are not implemented for GPU\n", + "AUC is not implemented on GPU. Will use CPU for metric computation, this could significantly affect learning time\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0:\ttest: 0.5831648\tbest: 0.5831648 (0)\ttotal: 115ms\tremaining: 9m 36s\n", + "500:\ttest: 0.6048420\tbest: 0.6048420 (500)\ttotal: 52.9s\tremaining: 7m 54s\n", + "1000:\ttest: 0.6071489\tbest: 0.6071489 (1000)\ttotal: 1m 45s\tremaining: 7m 1s\n", + "1500:\ttest: 0.6079927\tbest: 0.6080352 (1483)\ttotal: 2m 39s\tremaining: 6m 11s\n", + "bestTest = 0.6080667434\n", + "bestIteration = 1531\n", + "Shrink model to first 1532 iterations.\n" + ] + } + ], + "execution_count": 47 + }, + { + "cell_type": "code", + "id": "5d1522a7538db91b", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:32:03.696119Z", + "start_time": "2025-03-03T13:32:01.217024Z" + } + }, + "source": [ + "# score_df = train_data\n", + "# score_df['score'] = model.predict_proba(score_df[feature_columns_new])[:, -1]\n", + "# # score_df['score'] = model.predict(score_df[feature_columns_new])\n", + "# predictions_train = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", + "# # predictions_train = predictions_train[predictions_train['score'] > 0.]\n", + "# predictions_train[['trade_date', 'score', 'ts_code']].to_csv('predictions_train.tsv', index=False)" + ], + "outputs": [], + "execution_count": 48 + }, + { + "cell_type": "code", + "id": "a3d7e881-c9b7-48a9-ba7f-20cd28c33f37", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:32:04.089341Z", + "start_time": "2025-03-03T13:32:03.700197Z" + } + }, + "source": [ + "score_df = test_data\n", + "numeric_columns = score_df.select_dtypes(include=['float64', 'int64']).columns\n", + "score_df.loc[:, numeric_columns] = scaler.transform(score_df[numeric_columns])\n", + "score_df['score'] = model.predict_proba(score_df[feature_columns_new])[:, -1]\n", + "# score_df['score'] = model.predict(score_df[feature_columns_new])\n", + "predictions_test = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", + "# predictions_test = predictions_test[predictions_test['score'] > 0.5]\n", + "predictions_test[['trade_date', 'score', 'ts_code']].to_csv('predictions_test.tsv', index=False)" + ], + "outputs": [], + "execution_count": 49 + }, + { + "cell_type": "code", + "id": "ebae809e26a7b594", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:32:04.166384Z", + "start_time": "2025-03-03T13:32:04.160256Z" + } + }, + "source": [ + "print(predictions_test[predictions_test['trade_date'] > '2018-01-01'][['label', 'score', 'ts_code', 'trade_date']].head(\n", + " 10))" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " label score ts_code trade_date\n", + "43 3.0 0.409653 601666.SH 2024-03-01\n", + "962 4.0 0.305911 000680.SZ 2024-03-04\n", + "2012 3.0 0.336820 001270.SZ 2024-03-05\n", + "3379 4.0 0.329924 605111.SH 2024-03-06\n", + "4176 4.0 0.354170 605111.SH 2024-03-07\n", + "4991 4.0 0.326872 605398.SH 2024-03-08\n", + "5154 3.0 0.341976 603283.SH 2024-03-11\n", + "6000 0.0 0.355384 603283.SH 2024-03-12\n", + "6790 0.0 0.358717 603283.SH 2024-03-13\n", + "7507 0.0 0.350276 603283.SH 2024-03-14\n" + ] + } + ], + "execution_count": 50 + }, + { + "cell_type": "code", + "id": "751a6df9-d90b-4053-8769-c6c3b6654406", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-03T13:32:04.238552Z", + "start_time": "2025-03-03T13:32:04.235960Z" + } + }, + "source": [], + "outputs": [], + "execution_count": null + } + ], + "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.8.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/code/train/Rank.ipynb b/code/train/Rank.ipynb index 15ee3da..44a5187 100644 --- a/code/train/Rank.ipynb +++ b/code/train/Rank.ipynb @@ -2,18 +2,16 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, "id": "79a7758178bafdd3", "metadata": { - "ExecuteTime": { - "end_time": "2025-03-27T16:07:49.871896Z", - "start_time": "2025-03-27T16:07:49.868138Z" - }, "jupyter": { "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-31T14:48:00.476292Z", + "start_time": "2025-03-31T14:47:59.982503Z" } }, - "outputs": [], "source": [ "# %load_ext autoreload\n", "# %autoreload 2\n", @@ -24,19 +22,50 @@ "warnings.filterwarnings(\"ignore\")\n", "\n", "pd.set_option('display.max_columns', None)\n" - ] + ], + "outputs": [], + "execution_count": 1 }, { "cell_type": "code", - "execution_count": 2, "id": "a79cafb06a7e0e43", "metadata": { + "scrolled": true, "ExecuteTime": { - "end_time": "2025-03-27T16:08:46.495166Z", - "start_time": "2025-03-27T16:07:49.887412Z" - }, - "scrolled": true + "end_time": "2025-03-31T14:48:58.450443Z", + "start_time": "2025-03-31T14:48:00.733543Z" + } }, + "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", @@ -52,7 +81,7 @@ "cyq perf\n", "left merge on ['ts_code', 'trade_date']\n", "\n", - "RangeIndex: 8450470 entries, 0 to 8450469\n", + "RangeIndex: 8477357 entries, 0 to 8477356\n", "Data columns (total 31 columns):\n", " # Column Dtype \n", "--- ------ ----- \n", @@ -93,56 +122,17 @@ ] } ], - "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())" - ] + "execution_count": 2 }, { "cell_type": "code", - "execution_count": 3, "id": "cac01788dac10678", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:08:58.716303Z", - "start_time": "2025-03-27T16:08:46.882680Z" + "end_time": "2025-03-31T14:49:13.957935Z", + "start_time": "2025-03-31T14:48:58.472449Z" } }, - "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", @@ -190,22 +180,30 @@ "# 使用示例\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": 4, "id": "c4e9e1d31da6dba6", "metadata": { - "ExecuteTime": { - "end_time": "2025-03-27T16:08:59.028881Z", - "start_time": "2025-03-27T16:08:58.826823Z" - }, "jupyter": { "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-31T14:49:14.289341Z", + "start_time": "2025-03-31T14:49:14.053158Z" } }, - "outputs": [], "source": [ "def calculate_indicators(df):\n", " \"\"\"\n", @@ -282,19 +280,19 @@ "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", - "execution_count": 5, "id": "a735bc02ceb4d872", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:09:00.562306Z", - "start_time": "2025-03-27T16:08:59.257145Z" + "end_time": "2025-03-31T14:49:14.359139Z", + "start_time": "2025-03-31T14:49:14.310625Z" } }, - "outputs": [], "source": [ "import numpy as np\n", "import talib\n", @@ -595,23 +593,23 @@ "\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", - "execution_count": 6, "id": "53f86ddc0677a6d7", "metadata": { - "ExecuteTime": { - "end_time": "2025-03-27T16:09:06.284706Z", - "start_time": "2025-03-27T16:09:00.574333Z" - }, "jupyter": { "source_hidden": true }, - "scrolled": true + "scrolled": true, + "ExecuteTime": { + "end_time": "2025-03-31T14:49:20.203711Z", + "start_time": "2025-03-31T14:49:14.390412Z" + } }, - "outputs": [], "source": [ "from utils.factor import get_act_factor\n", "\n", @@ -658,18 +656,27 @@ "\n", "\n", "industry_df = read_industry_data('../../data/sw_daily.h5')\n" - ] + ], + "outputs": [], + "execution_count": 6 }, { "cell_type": "code", - "execution_count": 7, "id": "dbe2fd8021b9417f", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:09:06.368116Z", - "start_time": "2025-03-27T16:09:06.362512Z" + "end_time": "2025-03-31T14:49:20.248588Z", + "start_time": "2025-03-31T14:49:20.244295Z" } }, + "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", @@ -679,25 +686,20 @@ ] } ], - "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)" - ] + "execution_count": 7 }, { "cell_type": "code", - "execution_count": 8, "id": "85c3e3d0235ffffa", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:09:06.530886Z", - "start_time": "2025-03-27T16:09:06.449319Z" + "end_time": "2025-03-31T14:49:20.471125Z", + "start_time": "2025-03-31T14:49:20.288287Z" } }, + "source": [ + "print(df[df['is_st']][['ts_code', 'trade_date', 'is_st']])" + ], "outputs": [ { "name": "stdout", @@ -710,44 +712,27 @@ "96 000505.SZ 2017-01-03 True\n", "101 000511.SZ 2017-01-03 True\n", "... ... ... ...\n", - "8449447 603869.SH 2025-03-21 True\n", - "8449452 603879.SH 2025-03-21 True\n", - "8449499 603959.SH 2025-03-21 True\n", - "8449881 688282.SH 2025-03-21 True\n", - "8449885 688287.SH 2025-03-21 True\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", - "[192172 rows x 3 columns]\n" + "[192712 rows x 3 columns]\n" ] } ], - "source": [ - "print(df[df['is_st']][['ts_code', 'trade_date', 'is_st']])" - ] + "execution_count": 8 }, { "cell_type": "code", - "execution_count": 9, "id": "92d84ce15a562ec6", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:11:19.076831Z", - "start_time": "2025-03-27T16:09:06.609989Z" + "end_time": "2025-03-31T14:53:44.202296Z", + "start_time": "2025-03-31T14:51:08.550768Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Index: 5102787 entries, 0 to 5102786\n", - "Columns: 116 entries, ts_code to mv_momentum\n", - "dtypes: bool(12), datetime64[ns](2), float64(98), int32(1), int64(1), object(2)\n", - "memory usage: 4.0+ GB\n", - "None\n" - ] - } - ], "source": [ "def filter_data(df):\n", " # df = df.groupby('trade_date').apply(lambda x: x.nlargest(1000, 'act_factor1'))\n", @@ -757,6 +742,7 @@ " 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", + " df = df.drop(columns=['in_date'])\n", " df = df.reset_index(drop=True)\n", " return df\n", "\n", @@ -776,19 +762,32 @@ "# 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: 115 entries, ts_code to mv_momentum\n", + "dtypes: bool(12), datetime64[ns](1), float64(98), int32(1), int64(1), object(2)\n", + "memory usage: 4.0+ GB\n", + "None\n" + ] + } + ], + "execution_count": 10 }, { "cell_type": "code", - "execution_count": 10, "id": "b87b938028afa206", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:11:19.515797Z", - "start_time": "2025-03-27T16:11:19.496797Z" + "end_time": "2025-03-31T14:53:47.033579Z", + "start_time": "2025-03-31T14:53:45.267610Z" } }, - "outputs": [], "source": [ "from scipy.stats import ks_2samp, wasserstein_distance\n", "from sklearn.metrics import roc_auc_score\n", @@ -817,19 +816,19 @@ "\n", " return filtered_features, dropped_features\n", "\n" - ] + ], + "outputs": [], + "execution_count": 11 }, { "cell_type": "code", - "execution_count": 11, "id": "f4f16d63ad18d1bc", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:11:19.707351Z", - "start_time": "2025-03-27T16:11:19.695730Z" + "end_time": "2025-03-31T14:53:47.069023Z", + "start_time": "2025-03-31T14:53:47.064157Z" } }, - "outputs": [], "source": [ "def create_deviation_within_dates(df, feature_columns):\n", " groupby_col = 'cat_l2_code' # 使用 trade_date 进行分组\n", @@ -866,30 +865,19 @@ " # df[f'deviation_industry_{feature}'] = df[feature] - df[f'industry_{feature}']\n", "\n", " return df, ret_feature_columns\n" - ] + ], + "outputs": [], + "execution_count": 12 }, { "cell_type": "code", - "execution_count": 12, "id": "40e6b68a91b30c79", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:11:21.058403Z", - "start_time": "2025-03-27T16:11:19.827355Z" + "end_time": "2025-03-31T14:53:48.018649Z", + "start_time": "2025-03-31T14:53:47.170060Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ "import pandas as pd\n", "\n", @@ -1029,37 +1017,30 @@ "import gc\n", "\n", "gc.collect()" - ] + ], + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 13 }, { "cell_type": "code", - "execution_count": 13, "id": "47c12bb34062ae7a", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:14:38.093106Z", - "start_time": "2025-03-27T16:11:21.203458Z" + "end_time": "2025-03-31T15:05:41.683690Z", + "start_time": "2025-03-31T15:03:24.853004Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "feature_columns size: 106\n", - "去极值\n", - "去极值\n", - "检测到 17 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'vol_std_5', 'obv', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', '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', 'return_skew', 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout', 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio', 'cat_volume_ratio_breakout', 'vol_spike', 'atr_14', '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', '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", - "2539543\n", - "最小日期: 2018-06-04\n", - "最大日期: 2022-12-30\n", - "1232105\n", - "最小日期: 2023-01-03\n", - "最大日期: 2025-03-19\n" - ] - } - ], "source": [ "days = 2\n", "validation_days = 120\n", @@ -1103,7 +1084,7 @@ "\n", "\n", "train_data = df[filter_index & (df['trade_date'] <= '2023-01-01') & (df['trade_date'] >= '2000-01-01')]\n", - "test_data = df[filter_index & (df['trade_date'] >= '2023-01-01')]\n", + "test_data = df[(df['trade_date'] >= '2023-01-01')]\n", "\n", "\n", "def select_pre_zt_stocks_dynamic(stock_df):\n", @@ -1229,22 +1210,40 @@ "\n", "\n", "# feature_columns_new.remove('cat_l2_code')" - ] + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "feature_columns size: 106\n", + "去极值\n", + "去极值\n", + "检测到 17 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'vol_std_5', 'obv', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', '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', 'return_skew', 'return_kurtosis', 'volume_change_rate', 'cat_volume_breakout', 'turnover_deviation', 'cat_turnover_spike', 'avg_volume_ratio', 'cat_volume_ratio_breakout', 'vol_spike', 'atr_14', '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', '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", + "2540320\n", + "最小日期: 2018-06-04\n", + "最大日期: 2022-12-30\n", + "1252195\n", + "最小日期: 2023-01-03\n", + "最大日期: 2025-03-28\n" + ] + } + ], + "execution_count": 22 }, { "cell_type": "code", - "execution_count": 14, "id": "8f134d435f71e9e2", "metadata": { - "ExecuteTime": { - "end_time": "2025-03-27T16:14:38.583761Z", - "start_time": "2025-03-27T16:14:38.545794Z" - }, "jupyter": { "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-31T14:55:52.421019Z", + "start_time": "2025-03-31T14:55:50.668422Z" } }, - "outputs": [], "source": [ "from sklearn.preprocessing import StandardScaler\n", "import lightgbm as lgb\n", @@ -1388,63 +1387,19 @@ " model.fit(train_pool, eval_set=val_pool, plot=plot, use_best_model=True)\n", "\n", " return model, scaler\n" - ] + ], + "outputs": [], + "execution_count": 15 }, { "cell_type": "code", - "execution_count": 15, "id": "c6eb5cd4-e714-420a-ac48-39af3e11ee81", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:17:43.124162Z", - "start_time": "2025-03-27T16:14:38.735110Z" + "end_time": "2025-03-31T14:59:05.368441Z", + "start_time": "2025-03-31T14:55:52.438125Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train data size: 2539543\n", - "feature_contri: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n", - "原始训练集大小: 2539543\n", - "划分后的训练集大小: 2258414, 验证集大小: 281129\n", - "feature_columns size: 84\n", - "Training until validation scores don't improve for 50 rounds\n", - "[100]\ttrain's ndcg@1: 0.792371\tvalid's ndcg@1: 0.569953\n", - "Early stopping, best iteration is:\n", - "[132]\ttrain's ndcg@1: 0.814715\tvalid's ndcg@1: 0.623046\n", - "Evaluated only: ndcg@1\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHHCAYAAABDUnkqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAjn9JREFUeJzs3Xd8k9X+wPFPko5070VpKXvPMgSVoQzFAU4UVEBFZTguF7ey9Cde9SIOFO9VxKuiOBAHigICyt57zzK6S/dM8vz+OE3a0EFb2qbj+369+kpy8jxPTk4D+fac7zlHp2mahhBCCCFEA6F3dAWEEEIIIaqTBDdCCCGEaFAkuBFCCCFEgyLBjRBCCCEaFAluhBBCCNGgSHAjhBBCiAZFghshhBBCNCgS3AghhBCiQZHgRgghhBANigQ3QggWLVqETqfj9OnTNfYaM2fORKfT1ZvrOtrp06fR6XQsWrSoSufrdDpmzpxZrXUSor6Q4EaIWmQNInQ6HevXry/xvKZpREREoNPpuPnmm6v0Gh988EGVvxBF5SxevJh58+Y5uhpCiEtIcCOEAxiNRhYvXlyifN26dZw7dw5XV9cqX7sqwc39999PTk4OzZo1q/LrOspLL71ETk6OQ167JoObZs2akZOTw/3331+l83NycnjppZequVZC1A8S3AjhAMOHD+fbb7/FZDLZlS9evJjo6GhCQ0NrpR5ZWVkAGAwGjEZjvRresdbdyckJo9Ho4NpcXm5uLhaLpcLH63Q6jEYjBoOhSq9nNBpxcnKq0rlC1HcS3AjhAPfeey/JycmsXLnSVpafn893333H6NGjSz3HYrEwb948OnbsiNFoJCQkhEcffZSLFy/ajomKiuLAgQOsW7fONvw1cOBAoGhIbN26dUyaNIng4GCaNm1q99ylOTe//fYbAwYMwMvLC29vb3r16lVqj9Ol1q9fT69evTAajbRs2ZKPPvqoxDHl5ZRcmi9izas5ePAgo0ePxs/Pj2uuucbuuUvPnzJlCsuWLaNTp064urrSsWNHVqxYUeK11q5dS8+ePe3qWpE8noEDB7J8+XLOnDlja+uoqCjbNXU6HV9//TUvvfQS4eHhuLu7k56eTkpKCtOmTaNz5854enri7e3NjTfeyJ49ey7bPuPGjcPT05Pz588zcuRIPD09CQoKYtq0aZjN5gq14fHjxxk3bhy+vr74+Pgwfvx4srOz7c7NycnhiSeeIDAwEC8vL2699VbOnz8veTyi3pCwXggHiIqKom/fvnz11VfceOONgAok0tLSuOeee3j33XdLnPPoo4+yaNEixo8fzxNPPMGpU6d4//332bVrFxs2bMDZ2Zl58+bx+OOP4+npyYsvvghASEiI3XUmTZpEUFAQ06dPt/V+lGbRokU8+OCDdOzYkeeffx5fX1927drFihUrygzAAPbt28fQoUMJCgpi5syZmEwmZsyYUaIeVXHXXXfRunVrXnvtNTRNK/fY9evXs3TpUiZNmoSXlxfvvvsud9xxBzExMQQEBACwa9cubrjhBsLCwpg1axZms5nZs2cTFBR02bq8+OKLpKWlce7cOd5++20APD097Y555ZVXcHFxYdq0aeTl5eHi4sLBgwdZtmwZd911F82bNyc+Pp6PPvqIAQMGcPDgQZo0aVLu65rNZoYNG0afPn146623WLVqFf/+979p2bIlEydOvGy97777bpo3b86cOXPYuXMnH3/8McHBwfzrX/+yHTNu3Di++eYb7r//fq666irWrVvHTTfddNlrC1FnaEKIWvPpp59qgLZt2zbt/fff17y8vLTs7GxN0zTtrrvu0gYNGqRpmqY1a9ZMu+mmm2zn/f333xqgffnll3bXW7FiRYnyjh07agMGDCjzta+55hrNZDKV+typU6c0TdO01NRUzcvLS+vTp4+Wk5Njd6zFYin3PY4cOVIzGo3amTNnbGUHDx7UDAaDVvy/nFOnTmmA9umnn5a4BqDNmDHD9njGjBkaoN17770ljrU+d+n5Li4u2vHjx21le/bs0QDtvffes5Xdcsstmru7u3b+/Hlb2bFjxzQnJ6cS1yzNTTfdpDVr1qxE+Zo1azRAa9Gihe33a5Wbm6uZzWa7slOnTmmurq7a7Nmz7coubZ+xY8dqgN1xmqZp3bt316Kjo0u0QWlt+OCDD9odd9ttt2kBAQG2xzt27NAA7amnnrI7bty4cSWuKURdJcNSQjjI3XffTU5ODr/88gsZGRn88ssvZfaIfPvtt/j4+DBkyBCSkpJsP9HR0Xh6erJmzZoKv+6ECRMum8excuVKMjIyeO6550rks5Q3XGM2m/n9998ZOXIkkZGRtvL27dszbNiwCtexLI899liFjx08eDAtW7a0Pe7SpQve3t6cPHnSVtdVq1Yx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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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b775huvXr1OtWjUmTJjAO++8A0BmZib9+/dn165dXL9+HScnJwYOHMjQoUOfGseRI0fw9fVl1KhRjBkzRuf4m4TvJKOU2Qsff0lgpK8wozHUDvuV1ExNYYfz2pI86a6k5+rS9Dd1LluxYkWcnJwICQlh8eLFmJmZMWfOHK5evcq1a9e0yn711Vd88sknpKSkUL16daKjozE0NHzZ4QtRZEhDXxQr7777LoMHD2b37t20bdsWgLt377Jt2zaioqLYsGEDQ4cOZe7cuXh7e7NlyxZ69+5NxYoVad269Qvtc/To0cydO5eaNWsye/Zs/Pz8uHjxItbW1mqZsWPHMnPmTCpXrkzZsmUJDw9nxYoVfP3117i6urJv3z7ef/99bGxsaNWqFVlZWVSsWJEff/wRa2trDhw4QP/+/bG3t6dbt265Yti1axddunRhxowZWqevn5Samkpqaqr6PCkpCQAjPQV9feWFjr2kMNJTtH6KvEmedFfSc5XTS/80GRkZpKenk56eTqlSpVi1ahUDBw7EysoKfX192rZti4+PD4qiaNXVrVs3vLy8uH79OrNnz+bdd99l7969WmcDiqOcHDwvr6J45Co/sWsURSmZ7zKi2PL398fa2ppvv/0WyO7lnzRpEleuXKFly5bUqlWLb775Ri3frVs3UlJS2Lp1K5B9Me6GDRuee6HrpUuXqFSpEtOnT1d70DMyMqhUqRKDBw/mk08+UXv0N27cSOfOnYHsBreVlRU7duzA09NTra9v3748evSIVatW5bm/4OBgrl+/zk8//QRkX4x7//59AgMD6dWrF0uXLqV79+5PjTcsLIxJkyblWr5q1SpMTU2feaxCCFFQ/P39GTt2LE2bNs21LiUlhYyMDCwtLRk9ejRVq1blo48+yrOe9PR03n//fQYNGsQbb7zxqsMWosA8evSInj178uDBAywsLJ5ZVnr0RbETEBBAv379+OqrrzAyMmLlypW899576Onpcfbs2Vw93s2bN2fevHkvvL8nG+ulSpWiYcOGnD17VqtMw4YN1d/j4+N59OgR7dq10yqTlpaGh4eH+nzhwoUsW7aMxMRE/vnnH9LS0qhXr57WNocOHWLLli389NNPz/1iEhISwogRI9TnSUlJODo60rp1a62zDyK39PR0oqOjadeuHQYGBoUdzmtL8qQ7ydWzNWjQAF9f36fm6cKFCyQkJDB37txc76U5UlNT0dPTo2bNmvj6+hZU6IVCXk+6Kw65yjkjrwtp6Itix8/PD0VR2Lp1K40aNeK3335jzpw5hRqTmdn/jYFPTk4GYOvWrbnusGtkZATAmjVrGDVqFLNmzcLT05PSpUvzxRdfcOjQIa3yVapUUa89ePPNN5/5pmVkZKTW/yQDA4Mi+2ZX0CRXupE86U5ylS05OZn4+Hj1+ZUrVzh9+jSlS5cGYNOmTdjb2+Pk5MTJkycZOnQo/v7+agP+zz//5IcffqB9+/bY2Nhw9epVpk+fjomJCX5+fiUmx/J60l1RzlV+4pZZd0SxY2xsTJcuXVi5ciWrV6+mevXq1K9fHwA3NzdiYmK0ysfExFCzZs0X3t/vv/+u/p6RkcGxY8dwc3N7avmaNWtiZGREYmIiVatW1XrkzA4RExNDs2bNGDhwIB4eHlStWpWEhIRcdZUrV45du3YRHx9Pt27divSYQyFEyXX06FE8PDzUs5ojRozAw8NDHW54/fp1PvjgA2rUqMGQIUP44IMPWL16tbq9sbExv/32G76+vlStWpXu3btTunRpDhw4gK2tbaEckxCvA+nRF8VSQEAAnTp14vTp07z//vvq8tGjR9OtWzc8PDzw9vbm559/Zv369ezYseOF97Vw4UJcXV1xc3Njzpw53Lt3jw8//PCp5UuXLs2oUaMYPnw4WVlZtGjRggcPHhATE4OFhQWBgYG4urqyfPlyfv31VypVqsT333/PkSNHqFSpUq76bG1t2bVrF61bt6ZHjx6sWbOGUqXkX1sIUXR4eXmR1yWD6enpREVFERwczPDhw5+6vYODA1FRUa8yRCGKJOnRF8VSmzZtsLKyIi4ujp49e6rL/f39mTdvHjNnzqRWrVosXryYiIgIvLy8Xnhf06dPZ/r06bi7u7N//342b95MuXLlnrnN5MmTmTBhAuHh4bi5ueHj48PWrVvVhvxHH31Ely5d6N69O02aNOHOnTsMHDjwqfXZ2dmxa9cuTp48SUBAgNwJUgghhBAy644QJVVSUhKWlpbcvn1bLsZ9jpxeRV9f3yI7prMgSJ50J7nSjeRJN5In3RWHXOV8fusy64706AshhBBCCFEMSUNfiKeYNm0a5ubmeT46duxY2OEJIYQQQjyTXLEnxFMMGDAgz7vQApiYmBRwNEIIIYQQ+SMNfSGewsrKCisrq8IOQwghhBDihcjQHSGEEEIIIYohaeiLYsvFxYW5c+fqXP7SpUtoNBpiY2NfWUxCCCGy7du3Dz8/PxwcHNBoNGzcuFFr/Y0bN+jTpw+9e/fG0tISHx8fLly4oFXmm2++wcvLCwsLCzQaDffv3y+4AxCiCJCGvii2jhw5Qv/+/V9qnZGRkZQpU+al1vmiNBpNrseaNWsKOywhhNBJSkoK7u7uLFy4MNc6RVHw9/fn4sWLfPrppxw+fBhnZ2e8vb1JSUlRyz169AgfHx8+/fTTggxdiCJDxuiLYsvGxqawQ8hTWloahoaGL6WuiIgIfHx81Oevy5cQIYR4no4dOz51BrMLFy7w+++/c/z4cS5fvkz16tVZtGgRdnZ2rF69mr59+wIwbNgwAPbs2VNAUQtRtEiPvnhtbNmyhTJlyqh3dY2NjUWj0TB27Fi1TN++fXn//fcB2L9/Py1btsTExARHR0eGDBmi1dPz76E7586do0WLFhgbG1OzZk127NiR5+niP//8k9atW2Nqaoq7uzsHDx4Esj9IevfuzYMHD9Qe9LCwsOcel4uLC5MnT6ZXr15YWFioZxnWrVtHrVq1MDIywsXFhVmzZmltl5qaypgxY3B0dMTIyIiqVavy7bffapUpU6YMdnZ26sPY2Pi58QghxOsuNTUVQOs9TU9PDyMjI/bv319YYQlR5EiPvnhttGzZkocPH3L8+HEaNmzI3r17KVeunFZPzd69exkzZgwJCQn4+PgwZcoUli1bxq1btwgODiY4OJiIiIhcdWdmZuLv74+TkxOHDh3i4cOHjBw5Ms84xo0bx8yZM3F1dWXcuHH06NGD+Ph4mjVrxty5cwkNDSUuLg4Ac3NznY5t5syZhIaGMnHiRACOHTtGt27dCAsLo3v37hw4cICBAwdibW1NUFAQAL169eLgwYPMnz8fd3d3Ll68yO3bt7XqHTRoEH379qVy5coMGDCA3r17o9Fo8owhNTVV/fCE7DvrAbzx+Q4yDMx0Oo6SykhPYXJDaPDZNlKz8s6vkDzlR0nN1amwDk9dl5GRQXp6OgBVqlTBycmJcePG0aVLF1JSUvjqq6+4evUqf//9t1ruyW0h+66n/15XEuQcc0k89vwqDrnKT+zS0BevDUtLS+rVq8eePXto2LAhe/bsYfjw4UyaNInk5GQePHhAfHw8rVq1Ijw8nICAAPW0raurK/Pnz6dVq1YsWrQoV892dHQ0CQkJ7NmzBzs7OwCmTp1Ku3btcsUxatQo3nzzTQAmTZpErVq1iI+Pp0aNGlhaWqLRaNQ6dNWmTRutLxYBAQG0bduWCRMmAFCtWjXOnDnDF198QVBQEOfPn2ft2rVER0fj7e0NQOXKlbXq/Oyzz2jTpg2mpqZs376dgQMHkpyczJAhQ/KMITw8nEmTJuVaPt4jC1PTzHwdT0k1uWFWYYdQJEiedFfSchUVFfXUdceOHcPAwEB9PmTIEBYsWMD69evR09PD3d2d+vXrc+fOnVz1nDx5EoDt27fr3AFTHEVHRxd2CEVGUc7Vo0ePdC4rDX3xWmnVqhV79uxh5MiR/Pbbb4SHh7N27Vr279/P3bt3cXBwwNXVlRMnTvDHH3+wcuVKdVtFUcjKyuLixYu4ublp1RsXF4ejo6NWA71x48Z5xlC3bl31d3t7ewBu3rxJjRo1Xvi4GjZsqPX87NmzdO7cWWtZ8+bNmTt3LpmZmcTGxqKvr0+rVq2eWmfOlwQADw8PUlJS+OKLL57a0A8JCWHEiBHq86SkJBwdHZlyXI8MA/0XOawSI7v3NYsJR/VKVO9rfkmedFdSc/WsHv0GDRrg6+urtezjjz9m48aNNG/eHAcHB5o3b55nOTOz7LOS7du3L5HXKqWnpxMdHU27du20viyJ3IpDrnLOyOtCGvriteLl5cWyZcs4ceIEBgYG1KhRAy8vL/bs2cO9e/fUhm9ycjIfffRRno1aJyen/xTDk//4OcNgsrL+W69bzoeQrl7kzrtNmjRh8uTJpKamYmRklGu9kZFRnsv3jfHG2to63/srSdLT04mKiuJYqE+R/WAoCJIn3UmucitVqlSeuTAzM8PBwYFLly5x7NgxpkyZkqtcqVLZzRkDA4MSnc+Sfvz5UZRzlZ+4paEvXis54/TnzJmjNuq9vLyYPn069+7dU4e/1K9fnzNnzlC1alWd6q1evTpXrlzhxo0blC9fHsiefjO/DA0N1YuF/ws3NzdiYmK0lsXExFCtWjX09fWpU6cOWVlZ7N27Vx268zyxsbGULVs2z8a8EEK8bpKTk4mPj1efX7x4kdjYWKysrHBycuLHH3+kbNmyXL9+nc2bNzNy5Ej8/f1p3769us3169e5fv26Ws/JkycpXbo0Tk5OcmdzIZCGvnjNlC1blrp167Jy5UoWLFgAwBtvvEG3bt1IT09XG/9jxoyhadOmBAcH07dvX8zMzDhz5gzR0dHqdk9q164dVapUITAwkBkzZvDw4UPGjx8P8NSLV/Pi4uJCcnIyO3fuxN3dHVNTU0xNTfN9nCNHjqRRo0ZMnjyZ7t27c/DgQRYsWMBXX32l7icwMJAPP/xQvRj38uXL3Lx5k27duvHzzz9z48YNmjZtirGxMdHR0UybNo1Ro0blOxYhhCgMR48epXXr1urznKGFgYGBREZGcu3aNUaMGMH169dxcHCgV69eWkMWAb7++muta4/eeOMNIHvq4ZyJDYQoyWR6TfHaadWqFZmZmXh5eQFgZWVFzZo1sbOzo3r16kD2OPq9e/dy/vx5WrZsiYeHB6GhoTg4OORZp76+Phs3biQ5OZlGjRrRt29fxo0bB5CvKSmbNWvGgAED6N69OzY2NsyYMeOFjrF+/fqsXbuWNWvWULt2bUJDQ/nss8+0PpgWLVrEO++8w8CBA6lRowb9+vVTpw81MDBg4cKFeHp6Uq9ePRYvXszs2bPVWX2EEOJ15+XlhaIouR6RkZFA9sW4f/75Jz/99BPx8fFMnjw51z1IwsLC8qxDGvlCZNMoiqIUdhBCFIaYmBhatGhBfHw8VapUKexwClxSUhKWlpbcvn1bxug/R854al9f3yI7prMgSJ50J7nSjeRJN5In3RWHXOV8fj948AALC4tnlpWhO6LE2LBhA+bm5ri6uhIfH8/QoUNp3rx5iWzkCyGEEKL4k4a+KDEePnzImDFjSExMpFy5cnh7e+e6G21+/fbbb0+9hTtkX2wmhBBCCFEYpKEvSoxevXrRq1evl1pnw4YNiY2Nfal1CiGEEEK8DNLQF+I/MDEx0XmKTyGEEEKIgiSz7gghhBBCCFEMSUNfCCGEEK/Uvn378PPzw8HBAY1Gw8aNG7XWJycnExwcTMWKFTExMaFmzZp8/fXXWmWuX7/OBx98gJ2dHWZmZtSvX59169YV4FEIUfRIQ1+UGHv27EGj0XD//v2XVmdQUBD+/v6vTT1CCPE6SklJwd3dnYULF+a5fsSIEWzbto0VK1Zw9uxZhg0bRnBwMD///LNaplevXsTFxbF582ZOnjxJly5d6NatG8ePHy+owxCiyJEx+sWQl5cX9erVY+7cuYUdSrE3b9488nMrikuXLlGpUiWOHz9OvXr1XrgeIYQoSjp27PjMGcoOHDhAYGCgeqPE/v37s3jxYo4cOULTpk3VMosWLaJx48YAjB8/njlz5nDs2DE8PDxe+TEIURRJj77IU1paWmGH8Ezp6emFHQIAlpaWlClT5rWpRwghiqJmzZqxefNm/vrrLxRFYffu3Zw/f5527dpplfnhhx+4e/cuWVlZrFmzhsePH6tfDoQQuUmPfjETFBTE3r172bt3L/PmzQMgIiKCYcOGaQ1Z2bhxI2+//bbaixwWFsbGjRsJDg5m6tSpXL58maysLDQaDUuWLGHr1q38+uuvVKhQgVmzZvHWW2+pde3du5fRo0dz4sQJrKysCAwMZMqUKZQqVYpvvvmGsLAwrl69ip7e/32v7Ny5M9bW1ixbtgyATZs2MWnSJM6cOYODgwOBgYGMGzeOUqWyX6IajYavvvqKX375hZ07dzJ69GjCwsKemYuoqCiGDRvGlStXaNq0KYGBgbnK7N+/n5CQEI4ePUq5cuV4++23CQ8Px8zMjE8//ZSdO3dy6NAhrW3c3d3p2rUroaGhBAUFcf/+fXW86bZt25gyZQqnTp1CX18fT09P5s2bp96Uq1KlSgBq71OrVq3Ys2dPrnpSU1MZPXo0a9asISkpiYYNGzJnzhwaNWoEZA9Dat26NTt27GDMmDGcOXOGevXqERERQfXq1Z+Zl39rEr6TjFJm+dqmpDHSV5jRGGqH/Upqpqaww3ltSZ50VxJydWn6mzqX/fLLL+nfvz8VK1akVKlS6OnpsWTJElq2bElUVBQAa9eupXv37lhbW1OqVClMTU3ZsGGDzHwmxDNIQ7+YmTdvHufPn6d27dp89tlnAGzdulWnbePj41m3bh3r169HX19fXT5p0iRmzJjBF198wZdffklAQACXL1/GysqKv/76C19fX4KCgli+fDnnzp2jX79+GBsbExYWxrvvvsvgwYPZvXs3bdu2BeDu3bts27ZNffP+7bff6NWrF/Pnz6dly5YkJCTQv39/ACZOnKjGERYWxvTp05k7d676BeBprly5QpcuXRg0aBD9+/fn6NGjjBw5UqtMQkICPj4+TJkyhWXLlnHr1i2Cg4MJDg4mIiKCgIAAwsPDSUhIUBvqp0+f5o8//njqBWApKSmMGDGCunXrkpycTGhoKG+//TaxsbHo6elx+PBhGjduzI4dO6hVqxaGhoZ51vPJJ5+wbt06vvvuO5ydnZkxYwYdOnQgPj4eKysrtdy4ceOYNWsWNjY2DBgwgA8//JCYmJg860xNTSU1NVV9npSUBICRnoK+vgwbehYjPUXrp8ib5El3JSFXzzrzmpGRobV+7ty5HDx4kPXr1+Pk5MT+/fsZNGiQ+n6Xnp7OuHHjuHfvHtu2bcPa2prNmzfTrVs3du3aRZ06dV758bzOcnL5upztfp0Vh1zlJ3Zp6BczlpaWGBoaYmpqip2dHYBWo/1Z0tLSWL58OTY2NlrLg4KC6NGjBwDTpk1j/vz5HD58GB8fH7766iscHR1ZsGABGo2GGjVq8PfffzNmzBhCQ0MpW7YsHTt2ZNWqVWpD/6effqJcuXK0bt0ayP4iMXbsWLXHvXLlykyePJlPPvlEq6Hfs2dPevfurdOxLFq0iCpVqqh3vq1evTonT57k888/V8uEh4cTEBDAsGHDAHB1dWX+/Pm0atWKRYsWUatWLdzd3Vm1ahUTJkwAYOXKlTRp0uSpPUhdu3bVer5s2TJsbGw4c+YMtWvXVnNrbW2t/n3+LSUlhUWLFhEZGamOaV2yZAnR0dF8++23jB49Wi07depUWrVqBcDYsWN58803efz4McbGxrnqDQ8PZ9KkSbmWj/fIwtQ0M89YhLbJDbMKO4QiQfKku+Kcq5zOnLwcO3YMAwMDILsTYvz48YwdOxY9PT2uXr2Ki4sLTZs2JTQ0lIkTJ7J8+XK++uor5s+fz+PHj/nrr79o0KABzs7OfPrpp3z88ccFdVivtejo6MIOocgoyrl69OiRzmWloS9Uzs7OuRr5AHXr1lV/NzMzw8LCgps3bwJw9uxZPD090Wj+79Rz8+bNSU5O5urVqzg5OREQEEC/fv346quvMDIyYuXKlbz33nvqUJ4TJ04QExPD1KlT1ToyMzN5/Pgxjx49wtTUFMi+C62uzp49S5MmTbSWeXp6aj0/ceIEf/zxBytXrlSXKYpCVlYWFy9exM3NjYCAAJYtW8aECRNQFIXVq1czYsSIp+73woULhIaGcujQIW7fvk1WVvaHeGJiIrVr19Yp9oSEBNLT02nevLm6zMDAgMaNG3P27Fmtsk/+bezt7QG4efMmTk5OueoNCQnRij0pKQlHR0emHNcjw0C3L4MllZGewuSGWUw4qkdqVvEcZvEySJ50VxJydSqsw1PXNWjQAF9fXyD7vSgjI4PGjRvj4+OjltmyZYv6Hlq/fn0ge7ijm5ubWmbhwoVUrFhRraukSk9PJzo6mnbt2qlfoETeikOucs7I60Ia+iWAnp5erhld8jrtY2aW9zjtf/8jaDQa9c1XF35+fiiKwtatW2nUqBG//fYbc+bMUdcnJyczadIkunTpkmvbJ3umnxbfi0pOTuajjz5iyJAhudblNJR79OjBmDFj+N///sc///zDlStX6N69+1Pr9PPzw9nZmSVLluDg4EBWVha1a9d+ZRc3P/m3yfmy9bS/jZGREUZGRrmW7xvjjbW19SuJr7hIT08nKiqKY6E+RfaDoSBInnRX0nKVnJxMfHy8+vzKlSucPn0aKysrnJycaNWqFSEhIZQuXRpnZ2f27t3LihUr+OKLLwCoXbs2VatWJTg4mJkzZ2Jtbc3GjRvZsWMHW7ZsKRE51IWBgYHkQkdFOVf5iVsa+sWQoaEhmZn/NxTDxsaGhw8fkpKSojaWY2NjX8q+3NzcWLduHYqiqA3NmJgYSpcuTcWKFYHsxnqXLl1YuXIl8fHxVK9eXe2dgeyemri4uJd6QZWbmxubN2/WWvb7779rPa9fvz5nzpx55n4rVqxIq1atWLlyJf/88w/t2rXD1tY2z7J37twhLi5OvYAMsi/2fVLOmPwn/z7/VqVKFQwNDYmJicHZ2RnIbhQcOXJEHWYkhBBFydGjR9XhmoB6djEwMJDIyEjWrFlDSEgIAQEB3L17F2dnZ6ZOnUr//v355ZdfMDAwICoqirFjx+Ln50dycjJVq1blu+++K/G9+UI8izT0iyEXFxcOHTrEpUuXMDc3p0mTJpiamvLpp58yZMgQDh06RGRk5EvZ18CBA5k7dy6DBw8mODiYuLg4Jk6cyIgRI7Rm2QkICKBTp06cPn2a999/X6uO0NBQOnXqhJOTE++88w56enqcOHGCU6dOMWXKlBeKa8CAAcyaNYvRo0fTt29fjh07luuYx4wZQ9OmTQkODqZv376YmZlx5swZoqOjWbBggVbsEydOJC0tTetMxL+VLVsWa2trvvnmG+zt7UlMTGTs2LFaZWxtbTExMWHbtm1UrFgRY2NjLC0ttcqYmZnx8ccfM3r0aLW3a8aMGTx69Ig+ffq8UD6EEKIweXl5PfNeIXZ2dkRERORa/uTZZ1dXV7kTrhD5JPPoF0OjRo1CX1+fmjVrYmNjQ1JSEitWrCAqKoo6deqwevXq505NqasKFSoQFRXF4cOHcXd3Z8CAAfTp04fx48drlWvTpg1WVlbExcXRs2dPrXUdOnRgy5YtbN++nUaNGtG0aVPmzJmj9ma/CCcnJ9atW8fGjRtxd3fn66+/Ztq0aVpl6taty969ezl//jwtW7bEw8OD0NBQHBwctMq988473Llzh0ePHj3z7rV6enqsWbOGY8eOUbt2bYYPH66eds5RqlQp5s+fz+LFi3FwcKBz58551jV9+nS6du3KBx98QP369YmPj+fXX3+lbNmyL5YQIYQQQpQ4GkVuxylEiZSUlISlpSW3b9+WMfrPkTOe2tfXt8iO6SwIkifdSa50I3nSjeRJd8UhVzmf3w8ePMDCwuKZZaVHXwghhBBCiGJIGvqiSBowYADm5uZ5PgYMGFDY4QkhhBBCFDq5GFcUSZ999hmjRo3Kc93zTmMJIYQQQpQE0tAXRZKtre1Tp7kUQgghhBAydEcIIYQQQohiqUQ39C9duoRGo3lpN48Sr4aXl5fcKEoIIYqQffv24efnh4ODAxqNho0bN2qtT05OJjg4mIoVK2JiYkLNmjX5+uuvtcp4eXlhaGiIv78/hoaGaDQauQZLiHwqlg39oKCgZ853XpD27NmDRqPh/v37hR0KAGFhYWg0Gq1HjRo18l1PpUqV2LFjxwvH8brlxcXFhblz5xZ2GKq1a9dSr149TE1NcXZ2zjUfP2TnsH79+hgZGVG1atWXdhM0IYT4r1JSUnB3d2fhwoV5rh8xYgTbtm1jxYoVnD17lmHDhhEcHJzrjuZ9+vQhIiKCxMRErl27xowZMwoifCGKDRmj/4IURSEzM5NSpQouhenp6S9lztdatWppNdLzewx//PEH9+7do1WrVv85llctLS0NQ0PDIrW/X375hYCAAL788kvat2/P2bNn6devHyYmJgQHBwNw8eJF3nzzTQYMGMDKlSvZuXMnffv2xd7eng4dOryMQxFCiBfWsWNHOnbs+NT1Bw4cIDAwEC8vLwD69+/P4sWLOXz4MG+99ZZaztTUlLJly2JnZ1dk5zwXojAV6R79n376iTp16mBiYoK1tTXe3t6MHj2a7777jk2bNqk91nv27AHg8OHDeHh4YGxsTMOGDTl+/LjO+8rpgf7ll19o0KABRkZG7N+/n6ysLMLDw6lUqRImJia4u7vz008/AdlDg1q3bg1A2bJl0Wg0BAUFAXn3INerV0/rjrUajYZFixbx1ltvYWZmxtSpUwkLC6NevXp8//33uLi4YGlpyXvvvcfDhw91PpZSpUphZ2enPsqVK6fztgCbNm3Cx8fnuW+6ly9fxs/Pj7Jly2JmZkatWrWIiop6Zl5SUlLo1asX5ubm2NvbM2vWrHzF5uLiwuTJk+nVqxcWFhb0798fgP3799OyZUtMTExwdHRkyJAhpKSkANmnhy9fvszw4cPV1wyg5vpJc+fOxcXFRX2ec/Zo6tSpODg4UL16dXVI2Pr162ndujWmpqa4u7tz8OBBnY7h+++/x9/fnwEDBlC5cmXefPNNQkJC+Pzzz9VbyH/99ddUqlSJWbNm4ebmRnBwMO+88w5z5szJV76EEKIwNGvWjM2bN/PXX3+hKAq7d+/m/PnztG/fXqvc6tWr+eCDD6hXrx4hISE8evSokCIWomgqsj36165do0ePHsyYMYO3336bhw8f8ttvv9GrVy8SExNJSkoiIiICACsrK5KTk+nUqRPt2rVjxYoVXLx4kaFDh+Z7v2PHjmXmzJlUrlyZsmXLEh4ezooVK/j6669xdXVl3759vP/++9jY2NCiRQvWrVtH165diYuLw8LCAhMTk3ztLywsjOnTpzN37lxKlSrFsmXLSEhIYOPGjWzZsoV79+7RrVs3pk+fztSpU3Wq88KFCzg4OGBsbIynpyfh4eE4OTnpHNPmzZsZMWLEc8sNGjSItLQ09u3bh5mZGWfOnMHc3BxHR8en5mX06NHs3buXTZs2YWtry6effsr//ve/XA3uZ5k5cyahoaFMnDgRgISEBHx8fJgyZQrLli3j1q1bBAcHExwcTEREBOvXr8fd3Z3+/fvTr18/nfeTY+fOnVhYWBAdHa21fNy4ccycORNXV1fGjRtHjx49iI+Pf+4ZlNTUVExNTbWWmZiYcPXqVS5fvoyLiwsHDx7E29tbq0yHDh1e6FqGJuE7yShllu/tShIjfYUZjaF22K+kZmoKO5zXluRJd8UxV5emv6lz2S+//JL+/ftTsWJFSpUqhZ6eHkuWLOGNN95Qy/Ts2ZMKFSoQHx9PmTJlGDduHHFxcaxfv/5VhC9EsVSkG/oZGRl06dIFZ2dnAOrUqQNkN4pSU1Oxs7NTy0dGRpKVlcW3336LsbExtWrV4urVq3z88cf52u9nn31Gu3btgOwG2bRp09ixYweenp4AVK5cmf3797N48WJatWqFlZUVkD0dZJkyZfJ9nD179qR3795ay7KysoiMjKR06dIAfPDBB+zcuVOnhn6TJk2IjIykevXqXLt2jUmTJtGyZUtOnTql1vcsf/31F3/88cczT8nmSExMpGvXrurfpXLlyuq6vPKSnJzMt99+y4oVK2jbti0A3333HRUrVnzuvp7Upk0bRo4cqT7v27cvAQEBaiPY1dWV+fPn06pVKxYtWoSVlRX6+vqULl1a6zWjKzMzM5YuXaoO2bl06RIAo0aN4s03sz/4Jk2aRK1atYiPj3/uNREdOnRg+PDhBAUF0bp1a+Lj49UzG9euXcPFxYXr169Tvnx5re3Kly9PUlIS//zzT55fKFNTU0lNTVWfJyUlAWCkp6Cvr+T7uEsSIz1F66fIm+RJd8UxV+np6U9dl5GRobV+7ty5HDx4kPXr1+Pk5MT+/fsZNGgQtra26vt/7969SU9PJyMjg3bt2qlDE8+dO0eVKlVe+fEUJTm5fdbfQGQrDrnKT+xFtqHv7u5O27ZtqVOnDh06dKB9+/a88847lC1bNs/yZ8+epW7duhgbG6vLchrn+dGwYUP19/j4eB49eqQ2/HOkpaXh4eGR77qft78cLi4uWo1ye3t7bt68qVN9TzbQ69atS5MmTXB2dmbt2rX06dPnudtv3ryZFi1a6PSlZciQIXz88cds374db29vunbtSt26dZ9aPiEhgbS0NJo0aaIus7Kyonr16s/d15P+nbMTJ07wxx9/sHLlSnWZoihkZWVx8eJF3Nzc8lX/v9WpUyfPcflPHqu9vT0AN2/efG5Dv1+/fiQkJNCpUyfS09OxsLBg6NChhIWFoaf34qPtwsPDmTRpUq7l4z2yMDXNfOF6S5LJDbMKO4QiQfKku+KUq6ioqKeuO3bsmDrcMzU1lfHjxzN27Fj09PS4evUqLi4uNG3alE8//VQ9G/uk6OhoHj9+DMCaNWte2mdscfPvM8vi6YpyrvIzhK3INvT19fWJjo7mwIEDbN++nS+//JJx48Zx6NChV7pfM7P/G+KQnJwMwNatW6lQoYJWOSMjo2fWo6enp463zpHXN7Qn95fj32PjNRoNWVkv9mFRpkwZqlWrRnx8vE7lN2/erHWh1LP07duXDh06sHXrVrZv3054eDizZs1i8ODBLxSrrv6ds+TkZD766COGDBmSq+yzhiz9l78RaP+dcsb96/J30mg0fP7550ybNo3r169jY2PDzp07gf87K2JnZ8eNGze0trtx48Yzh4eFhIRoDblKSkrC0dGR1q1bY21t/dy4SrL09HSio6Np166dXBD4DJIn3ZW0XDVo0ABfX18g+70nIyODxo0b4+Pjo5bZsmULgFoOtPN05MgRAPz8/J7ZaVQSlbTX039RHHKVc0ZeF0W2oQ/ZDaLmzZvTvHlzQkNDcXZ2ZsOGDRgaGpKZqd1D6ebmxvfff8/jx4/VXv3ff//9P+2/Zs2aGBkZkZiY+NQZaHJ6ev8dj42NDdeuXVOfJyUlcfHixf8Uz4tITk4mISGBDz74QKeyu3fvZtGiRTrX7+joyIABAxgwYAAhISEsWbKEwYMH55mXKlWqYGBgwKFDh9QG+L179zh//vx/muGnfv36nDlzhqpVqz61TF6vGRsbG65fv46iKGpDvSDvuaCvr69+gVy9ejWenp7Y2NgA2Wej/t17Fh0d/cyzVEZGRnl+ATUwMCiyb3YFTXKlG8mT7oprrpKTk7U6kK5cucLp06exsrLCycmJVq1aERISQunSpXF2dmbv3r2sWLGC2bNnY2BgQEJCAqtWraJ9+/bcuHGDbdu2MXr0aN544w0aNGhQiEf2eiuur6dXoSjnKj9xF9mG/qFDh9i5cyft27fH1taWQ4cOcevWLdzc3Hj8+DG//vorcXFxWFtbY2lpSc+ePRk3bhz9+vUjJCSES5cuMXPmzP8UQ+nSpRk1ahTDhw8nKyuLFi1a8ODBA2JiYrCwsCAwMBBnZ2c0Gg1btmzB19cXExMTzM3NadOmDZGRkfj5+VGmTBlCQ0PR19d/Sdl5ulGjRuHn54ezszN///03EydORF9fnx49ejx3223btlGtWjWtWWeeZdiwYXTs2JFq1apx7949du/erQ6TeVpe+vTpw+jRo7G2tsbW1pZx48b9p+EqAGPGjKFp06YEBwfTt29f9cLg6OhoFixYAGQPh9q3bx/vvfceRkZGlCtXDi8vL27dusWMGTN455132LZtG7/88gsWFhb/KZ7nuX37Nj/99BNeXl48fvyYiIgIfvzxR/bu3auWGTBgAAsWLOCTTz7hww8/ZNeuXaxdu5atW7e+0tiEEEIXR48eVWdXA9SziYGBgURGRrJmzRpCQkIICAjg7t27ODs7M3XqVPWGWIaGhuzYsYO5c+fy8OFDnJ2d6dq1K+PHjy+U4xGiyFKKqDNnzigdOnRQbGxsFCMjI6VatWrKl19+qSiKoty8eVNp166dYm5urgDK7t27FUVRlIMHDyru7u6KoaGhUq9ePWXdunUKoBw/fvy5+9u9e7cCKPfu3dNanpWVpcydO1epXr26YmBgoNjY2CgdOnRQ9u7dq5b57LPPFDs7O0Wj0SiBgYGKoijKgwcPlO7duysWFhaKo6OjEhkZqbi7uysTJ05UtwOUDRs2aO1v4sSJiru7u9ayOXPmKM7OzjpkTVG6d++u2NvbK4aGhkqFChWU7t27K/Hx8Tpt+/777yvjxo3TqayiKEpwcLBSpUoVxcjISLGxsVE++OAD5fbt2+r6vPLy8OFD5f3331dMTU2V8uXLKzNmzFBatWqlDB06VKd9Ojs7K3PmzMm1/PDhw+prwszMTKlbt64ydepUdf3BgweVunXrKkZGRsqT/xaLFi1SHB0dFTMzM6VXr17K1KlTtXIdGBiodO7cWWtfFy9ezPW6unfvntZr8Vlu3bqlNG3aVDEzM1NMTU2Vtm3bKr///nuucrt371bq1aunGBoaKpUrV1YiIiKeW/eTHjx4oABafxORt7S0NGXjxo1KWlpaYYfyWpM86U5ypRvJk24kT7orDrnK+fx+8ODBc8tqFEUpPpf8i1cmIyOD8uXL88svv9C4cePCDke8BElJSVhaWnL79m0Zo/8c6enpREVF4evrW2RP9RYEyZPuJFe6kTzpRvKku+KQq5zP7wcPHjx3lEGRvmGWKDh3795l+PDhNGrUqLBDEUIIIYQQOpCG/v83YMAAzM3N83zkjBl83SUmJj71GMzNzUlMTHzm9itXrnzqtq1bt2b8+PHqRamQPVXn08pPmzbtpR/fb7/99szjKyoKOm9CCCGEKJmK7MW4L9tnn33GqFGj8lz3qi++fFkcHByeOSuMg4PDM7d/6623tOawf1Jep7eWLl3KP//8k2f5nBtivUwNGzYs0FlvXpWCzpsQQgghSiZp6P9/tra22NraFnYY/0mpUqWeOYXk85QuXVqnu+Pm+Pe9A141ExOT/3R8r4uCzpsQQgghSiYZuiOEEEIIIUQxJA19IYQQQuTLvn378PPzw8HBAY1Gw8aNG7XWJycnExwcTMWKFTExMaFmzZp8/fXXWmUeP37MoEGDsLa2xtzcnK5du+a647cQ4r+Rhr7QSVBQEP7+/s8s4+XlxbBhwwokHiGEEIUnJSUFd3d3Fi5cmOf6ESNGsG3bNlasWMHZs2cZNmwYwcHBbN68WS0zfPhwfv75Z/WGgH///TddunQpqEMQokSQhn4RExYWRr169Qo7DCGEECVYx44dmTJlCm+//Xae6w8cOEBgYCBeXl64uLjQv39/3N3dOXz4MAAPHjzg22+/Zfbs2bRp04YGDRoQERHBgQMH+P333wvyUIQo1qShL4qNtLS0wg5BCCEE0KxZMzZv3sxff/2Foijs3r2b8+fP0759ewCOHTtGeno63t7e6jY1atTAycmJgwcPFlbYQhQ7MutOIcjKymLmzJl88803XLlyhfLly/PRRx8xbtw4xowZw4YNG7h69Sp2dnYEBAQQGhqKgYEBkZGRTJo0CUCdzz4iIoKgoKBn7u/cuXP07duXo0ePUrlyZebPn0+7du3YsGGDOhzn5MmTDB06lIMHD2JqakrXrl2ZPXv2U+enT0lJ4eOPP2b9+vWULl06z6lJU1NTGTduHKtXr+b+/fvUrl2bzz//HC8vLwAiIyMZNmwYP/zwA8OGDePKlSu0aNGCiIgI7O3tn5vHoKAg7t+/T6NGjVi4cCFGRkZcvHiRK1euMHLkSLZv346enh4tW7Zk3rx5uLi4ALBnzx4++eQTTp8+jYGBAbVq1WLVqlU4OzsDsGjRImbOnMmVK1eoVKkS48eP54MPPlD3q9FoWLJkCVu3buXXX3+lQoUKzJo1i7feeguAzMxM+vfvz65du7h+/TpOTk4MHDiQoUOH5oq9RYsWzJo1i7S0NN577z3mzp2rTmWamppKaGgoq1at4ubNmzg6OhISEkKfPn0AOHXqFKNHj+a3337DzMyM9u3bM2fOHMqVK/fc3D2pSfhOMkqZ5WubksZIX2FGY6gd9iupmZrnb1BCSZ50VxRzdWn6mzqX/fLLL+nfvz8VK1akVKlS6OnpsWTJEt544w0Arl+/jqGhIWXKlNHarnz58ly/fv1lhi1EiSYN/UIQEhLCkiVLmDNnDi1atODatWucO3cOyJ7iMjIyEgcHB06ePEm/fv0oXbo0n3zyCd27d+fUqVNs27aNHTt2AGBpafnMfWVmZuLv74+TkxOHDh3i4cOHjBw5UqtMSkoKHTp0wNPTkyNHjnDz5k369u1LcHAwkZGRedY7evRo9u7dy6ZNm7C1teXTTz/lf//7n9awouDgYM6cOcOaNWtwcHBgw4YN+Pj4cPLkSVxdXQF49OgRM2fO5Pvvv0dPT4/333+fUaNGsXLlSp1yuXPnTiwsLIiOjgayb22dcyy//fYbpUqVYsqUKfj4+PDHH3+gp6eHv78//fr1Y/Xq1aSlpXH48GH1i9OGDRsYOnQoc+fOxdvbmy1bttC7d28qVqxI69at1f1OmjSJGTNm8MUXX/Dll18SEBDA5cuXsbKyIisri4oVK/Ljjz9ibW3NgQMH6N+/P/b29nTr1k2tY/fu3djb27N7927i4+Pp3r079erVo1+/fgD06tWLgwcPMn/+fNzd3bl48SK3b98G4P79+7Rp04a+ffsyZ84c/vnnH8aMGUO3bt3YtWtXnrlKTU0lNTVVfZ6UlASAkZ6Cvr6iU75LKiM9ReunyJvkSXdFMVfp6elPXZeRkaG1fu7cuRw8eJD169fj5OTE/v37GTRoELa2trRt25aMjIw861QUhczMTHX5v3+KvEmedFcccpWf2DWKohSdd5li4OHDh9jY2LBgwQL69u373PIzZ85kzZo1HD16FMgeo79x40adbxy1bds2/Pz8uHLlCnZ2dgDs2LFDq0d/yZIljBkzhitXrmBmlt2zGxUVhZ+fH3///Tfly5dXe6A3btxIcnIy1tbWrFixgnfffReAu3fvUrFiRfr378/cuXNJTEykcuXKJCYmat2oy9vbm8aNGzNt2jQiIyPp3bs38fHxVKlSBYCvvvqKzz77TKcenaCgILZt20ZiYiKGhoYArFixgilTpnD27Fm18Z6WlkaZMmXYuHEjDRs2xNramj179tCqVatcdTZv3pxatWrxzTffqMu6detGSkoKW7duBbJ79MePH8/kyZOB7C9K5ubm/PLLL/j4+OQZa3BwMNevX+enn35SY9+zZw8JCQno6+ur+9HT02PNmjWcP3+e6tWrEx0drXVqO8eUKVP47bff+PXXX9VlV69exdHRkbi4OKpVq5Zrm7CwMPWM0JNWrVqFqalpnnELIcTz+Pv7M3bsWJo2bQpkdyoEBAQwduxYGjZsqJZbsGABd+7cYeLEifzxxx+EhoayYsUKrTPH/fr1w8/PTz1DKoTI7dGjR/Ts2ZMHDx4896au0qNfwM6ePUtqaipt27bNc/0PP/zA/PnzSUhIIDk5mYyMjP90Z964uDgcHR3VRj5A48aNc8Xk7u6uNvIhu8GblZVFXFwc5cuX1yqfkJBAWlqa1l10raysqF69uvr85MmTZGZm5mpwpqamYm1trT43NTVVG/kA9vb23Lx5U+fjq1OnjtrIBzhx4gTx8fG5bvz1+PFjEhISaN++PUFBQXTo0IF27drh7e1Nt27d1KFCZ8+epX///lrbNm/enHnz5mktq1u3rvq7mZkZFhYWWnEvXLiQZcuWkZiYyD///ENaWlqui6hr1aqlNvJzjv3kyZMAxMbGoq+vn+eXkZzj3L17d55DqxISEvJs6IeEhDBixAj1eVJSEo6Ojkw5rkeGgX6u8uL/GOkpTG6YxYSjeqRmFY1hFoVB8qS7opirU2EdnrquQYMG+Pr6AtnvLRkZGTRu3Fir82PLli0A+Pr60rx5cyZPnkypUqXU7eLi4rh16xa9e/dWP1/S09OJjo6mXbt2ed6hXWSTPOmuOOQq54y8LqShX8BMTEyeuu7gwYMEBAQwadIkOnTogKWlJWvWrGHWrFkFGOHLkZycjL6+PseOHdNqzAJajdN//5NpNBryc5LpyS8nOftt0KBBnkN/bGxsgOzrGoYMGcK2bdv44YcfGD9+PNHR0WpvlC7yijsrKwuANWvWMGrUKGbNmoWnpyelS5fmiy++4NChQzrX8azXSc5x+vn58fnnn+da97TrG4yMjDAyMsq1fN8Yb60vXyK39PR0oqKiOBbqU2Q/GAqC5El3RT1XycnJxMfHq8+vXLnC6dOnsbKywsnJiVatWhESEkLp0qVxdnZm7969rFixgtmzZ2NgYEC5cuXo06cPn3zyCba2tlhYWDB48GA8PT1p0aJFrv0ZGBgUyTwVNMmT7opyrvITtzT0C5irqysmJibs3Lkz19CdAwcO4OzszLhx49Rlly9f1ipjaGhIZmamzvurXr06V65c4caNG2rP/JEjR7TKuLm5ERkZSUpKitpwjomJQU9PT6uXPkeVKlUwMDDg0KFDODk5AXDv3j3Onz+v9kB7eHiQmZnJzZs3admypc7x/lf169fnhx9+UD84nsbDwwMPDw9CQkLw9PRk1apVNG3aFDc3N2JiYggMDFTLxsTEULNmTZ1jiImJoVmzZgwcOFBdlpCQkK/jqFOnDllZWezduzfPoTv169dn3bp1uLi4UKqU/BsLIQrW0aNHta5byjlbGBgYSGRkJGvWrCEkJISAgADu3r2Ls7MzU6dOZcCAAeo2c+bMQU9Pj65du5KamkqHDh346quvCvxYhCjOpIVQwIyNjRkzZgyffPIJhoaGNG/enFu3bnH69GlcXV1JTExkzZo1NGrUiK1bt7Jhwwat7V1cXLh48SKxsbFUrFiR0qVL59lLm6Ndu3ZUqVKFwMBAZsyYwcOHDxk/fjzwfzP3BAQEMHHiRAIDAwkLC+PWrVsMHjyYDz74INewHcjuke/Tpw+jR4/G2toaW1tbxo0bh57e/83WWq1aNQICAujVqxezZs3Cw8ODW7dusXPnTurWrcubb+o+e0N+BAQE8MUXX9C5c2c+++wzKlasyOXLl1m/fj2ffPIJ6enpfPPNN7z11ls4ODgQFxfHhQsX6NWrF5B9kXG3bt3w8PDA29ubn3/+mfXr16sXP+vC1dWV5cuX8+uvv1KpUiW+//57jhw5QqVKlXSuw8XFhcDAQD788EP1YtzLly9z8+ZNunXrxqBBg1iyZAk9evTgk08+wcrKivj4eNasWcPSpUtznUURQoiXycvL65lnX+3s7IiIiHhmHcbGxixcuPCpN90SQvx3Mo9+IZgwYQIjR44kNDQUNzc3unfvzs2bN3nrrbcYPnw4wcHB1KtXjwMHDjBhwgStbbt27YqPjw+tW7fGxsaG1atXP3Nf+vr66gW0jRo1om/fvuoZA2NjYyB7nPyvv/7K3bt3adSoEe+88w5t27ZlwYIFT633iy++oGXLlvj5+eHt7U2LFi1o0KCBVpmIiAh69erFyJEjqV69Ov7+/hw5ckQ9C/AqmJqasm/fPpycnOjSpQtubm706dOHx48fY2FhgampKefOnaNr165Uq1aN/v37M2jQID766CMg+6KyefPmMXPmTGrVqsXixYuJiIhQpwTVxUcffUSXLl3o3r07TZo04c6dO1q9+7patGgR77zzDgMHDqRGjRr069ePlJQUABwcHIiJiSEzM5P27dtTp04dhg0bRpkyZbS+cAkhhBCi5JJZd0qgmJgYWrRooTXbjSh5kpKSsLS05Pbt2zJG/zlyxlP7+voW2TGdBUHypDvJlW4kT7qRPOmuOOQq5/NbZt0RQPbc8Obm5ri6uhIfH8/QoUNp3ry5NPKFEEIIIYoxOcdfxK1cuRJzc/M8H7Vq1QKy5+4fNGgQNWrUICgoiEaNGrFp06ZCjvz5nnZc5ubm/Pbbb4UdnhBCCCHEa0169Iu4t956S2s++yflnJLq1auXerFpUfKsm4JVqFCh4AIRQgghhCiCpKFfxJUuXTrXzaGKi6pVqxZ2CEIIIYQQRZYM3RFCCCGEEKIYkoa+EEIIIYQQxZA09IUQQgghhCiGpKEvhBBCCCFEMSQNfSGEEEIIIYohaegLIYQQQghRDMn0mkKUUIqiANk3VCuqtwEvKOnp6Tx69IikpCTJ1TNInnQnudKN5Ek3kifdFYdcJSUlAf/3Of4s0tAXooS6c+cOAJUqVSrkSIQQQgiRXw8fPsTS0vKZZaShL0QJZWVlBUBiYuJz3yhKuqSkJBwdHbly5QoWFhaFHc5rS/KkO8mVbiRPupE86a445EpRFB4+fIiDg8Nzy0pDX4gSSk8v+xIdS0vLIvtmV9AsLCwkVzqQPOlOcqUbyZNuJE+6K+q50rWDTi7GFUIIIYQQohiShr4QQgghhBDFkDT0hSihjIyMmDhxIkZGRoUdymtPcqUbyZPuJFe6kTzpRvKku5KWK42iy9w8QgghhBBCiCJFevSFEEIIIYQohqShL4QQQgghRDEkDX0hhBBCCCGKIWnoCyGEEEIIUQxJQ1+IEmrhwoW4uLhgbGxMkyZNOHz4cGGHVKjCwsLQaDRajxo1aqjrHz9+zKBBg7C2tsbc3JyuXbty48aNQoy4YOzbtw8/Pz8cHBzQaDRs3LhRa72iKISGhmJvb4+JiQne3t5cuHBBq8zdu3cJCAjAwsKCMmXK0KdPH5KTkwvwKArG83IVFBSU6zXm4+OjVaYk5Co8PJxGjRpRunRpbG1t8ff3Jy4uTquMLv9viYmJvPnmm5iammJra8vo0aPJyMgoyEN5pXTJk5eXV67X1IABA7TKFPc8ASxatIi6deuqN8Hy9PTkl19+UdeX5NeTNPSFKIF++OEHRowYwcSJE/nf//6Hu7s7HTp04ObNm4UdWqGqVasW165dUx/79+9X1w0fPpyff/6ZH3/8kb179/L333/TpUuXQoy2YKSkpODu7s7ChQvzXD9jxgzmz5/P119/zaFDhzAzM6NDhw48fvxYLRMQEMDp06eJjo5my5Yt7Nu3j/79+xfUIRSY5+UKwMfHR+s1tnr1aq31JSFXe/fuZdCgQfz+++9ER0eTnp5O+/btSUlJUcs87/8tMzOTN998k7S0NA4cOMB3331HZGQkoaGhhXFIr4QueQLo16+f1mtqxowZ6rqSkCeAihUrMn36dI4dO8bRo0dp06YNnTt35vTp00AJfz0pQogSp3HjxsqgQYPU55mZmYqDg4MSHh5eiFEVrokTJyru7u55rrt//75iYGCg/Pjjj+qys2fPKoBy8ODBAoqw8AHKhg0b1OdZWVmKnZ2d8sUXX6jL7t+/rxgZGSmrV69WFEVRzpw5owDKkSNH1DK//PKLotFolL/++qvAYi9o/86VoihKYGCg0rlz56duU1JzdfPmTQVQ9u7dqyiKbv9vUVFRip6ennL9+nW1zKJFixQLCwslNTW1YA+ggPw7T4qiKK1atVKGDh361G1KYp5ylC1bVlm6dGmJfz1Jj74QJUxaWhrHjh3D29tbXaanp4e3tzcHDx4sxMgK34ULF3BwcKBy5coEBASQmJgIwLFjx0hPT9fKWY0aNXBycirRObt48SLXr1/XyoulpSVNmjRR83Lw4EHKlClDw4YN1TLe3t7o6elx6NChAo+5sO3ZswdbW1uqV6/Oxx9/zJ07d9R1JTVXDx48AMDKygrQ7f/t4MGD1KlTh/Lly6tlOnToQFJSktqLW9z8O085Vq5cSbly5ahduzYhISE8evRIXVcS85SZmcmaNWtISUnB09OzxL+eShV2AEKIgnX79m0yMzO13tAAypcvz7lz5wopqsLXpEkTIiMjqV69OteuXWPSpEm0bNmSU6dOcf36dQwNDSlTpozWNuXLl+f69euFE/BrIOfY83ot5ay7fv06tra2WutLlSqFlZVVicudj48PXbp0oVKlSiQkJPDpp5/SsWNHDh48iL6+fonMVVZWFsOGDaN58+bUrl0bQKf/t+vXr+f5ustZV9zklSeAnj174uzsjIODA3/88QdjxowhLi6O9evXAyUrTydPnsTT05PHjx9jbm7Ohg0bqFmzJrGxsSX69SQNfSGEADp27Kj+XrduXZo0aYKzszNr167FxMSkECMTxcV7772n/l6nTh3q1q1LlSpV2LNnD23bti3EyArPoEGDOHXqlNb1MCK3p+Xpyes36tSpg729PW3btiUhIYEqVaoUdJiFqnr16sTGxvLgwQN++uknAgMD2bt3b2GHVehk6I4QJUy5cuXQ19fPNePAjRs3sLOzK6SoXj9lypShWrVqxMfHY2dnR1paGvfv39cqU9JzlnPsz3ot2dnZ5brIOyMjg7t375bo3AFUrlyZcuXKER8fD5S8XAUHB7NlyxZ2795NxYoV1eW6/L/Z2dnl+brLWVecPC1PeWnSpAmA1muqpOTJ0NCQqlWr0qBBA8LDw3F3d2fevHkl/vUkDX0hShhDQ0MaNGjAzp071WVZWVns3LkTT0/PQozs9ZKcnExCQgL29vY0aNAAAwMDrZzFxcWRmJhYonNWqVIl7OzstPKSlJTEoUOH1Lx4enpy//59jh07ppbZtWsXWVlZaqOkpLp69Sp37tzB3t4eKDm5UhSF4OBgNmzYwK5du6hUqZLWel3+3zw9PTl58qTWF6Po6GgsLCyoWbNmwRzIK/a8POUlNjYWQOs1Vdzz9DRZWVmkpqbK66mwrwYWQhS8NWvWKEZGRkpkZKRy5swZpX///kqZMmW0ZhwoaUaOHKns2bNHuXjxohITE6N4e3sr5cqVU27evKkoiqIMGDBAcXJyUnbt2qUcPXpU8fT0VDw9PQs56lfv4cOHyvHjx5Xjx48rgDJ79mzl+PHjyuXLlxVFUZTp06crZcqUUTZt2qT88ccfSufOnZVKlSop//zzj1qHj4+P4uHhoRw6dEjZv3+/4urqqvTo0aOwDumVeVauHj58qIwaNUo5ePCgcvHiRWXHjh1K/fr1FVdXV+Xx48dqHSUhVx9//LFiaWmp7NmzR7l27Zr6ePTokVrmef9vGRkZSu3atZX27dsrsbGxyrZt2xQbGxslJCSkMA7plXhenuLj45XPPvtMOXr0qHLx4kVl06ZNSuXKlZU33nhDraMk5ElRFGXs2LHK3r17lYsXLyp//PGHMnbsWEWj0Sjbt29XFKVkv56koS9ECfXll18qTk5OiqGhodK4cWPl999/L+yQClX37t0Ve3t7xdDQUKlQoYLSvXt3JT4+Xl3/zz//KAMHDlTKli2rmJqaKm+//bZy7dq1Qoy4YOzevVsBcj0CAwMVRcmeYnPChAlK+fLlFSMjI6Vt27ZKXFycVh137txRevTooZibmysWFhZK7969lYcPHxbC0bxaz8rVo0ePlPbt2ys2NjaKgYGB4uzsrPTr1y/Xl+uSkKu8cgQoERERahld/t8uXbqkdOzYUTExMVHKleRob1YAAAowSURBVCunjBw5UklPTy/go3l1npenxMRE5Y033lCsrKwUIyMjpWrVqsro0aOVBw8eaNVT3POkKIry4YcfKs7OzoqhoaFiY2OjtG3bVm3kK0rJfj1pFEVRCu78gRBCCCGEEKIgyBh9IYQQQgghiiFp6AshhBBCCFEMSUNfCCGEEEKIYkga+kIIIYQQQhRD0tAXQgghhBCiGJKGvhBCCCGEEMWQNPSFEEIIIYQohqShL4QQQhQhXl5eDBs2rLDDEEIUAdLQF0IIUWwEBQWh0WhyPeLj419K/ZGRkZQpU+al1PWi1q9fz+TJkws1hmfZs2cPGo2G+/fvF3YoQpR4pQo7ACGEEOJl8vHxISIiQmuZjY1NIUXzdOnp6RgYGOR7Oysrq1cQzcuRnp5e2CEIIZ4gPfpCCCGKFSMjI+zs7LQe+vr6AGzatIn69etjbGxM5cqVmTRpEhkZGeq2s2fPpk6dOpiZmeHo6MjAgQNJTk4Gsnuqe/fuzYMHD9QzBWFhYQBoNBo2btyoFUeZMmWIjIwE4NKlS2g0Gn744QdatWqFsbExK1euBGDp0qW4ublhbGxMjRo1+Oqrr555fP8euuPi4sKUKVPo1asX5ubmODs7s3nzZm7dukXnzp0xNzenbt26HD16VN0m58zExo0bcXV1xdjYmA4dOnDlyhWtfS1atIgqVapgaGhI9erV+f7777XWazQaFi1axFtvvYWZmRn9+vWjdevWAJQtWxaNRkNQUBAA27Zto0WLFpQpUwZra2s6depEQkKCWldOjtavX0/r1q0xNTXF3d2dgwcPau0zJiYGLy8vTE1NKVu2LB06dODevXsAZGVlER4eTqVKlTAxMcHd3Z2ffvrpmfkUolhThBBCiGIiMDBQ6dy5c57r9u3bp1hYWCiRkZFKQkKCsn37dsXFxUUJCwtTy8yZM0fZtWuXcvHiRWXnzp1K9erVlY8//lhRFEVJTU1V5s6dq1hYWCjXrl1Trl27pjx8+FBRFEUBlA0bNmjtz9LSUomIiFAURVEuXryoAIqLi4uybt065c8//1T+/vtvZcWKFYq9vb26bN26dYqVlZUSGRn51GNs1aqVMnToUPW5s7OzYmVlpXz99dfK+fPnlY8//lixsLBQfHx8lLVr1ypxcXGKv7+/4ubmpmRlZSmKoigRERGKgYGB0rBhQ+XAgQPK0aNHlcaNGyvNmjVT612/fr1iYGCgLFy4UImLi1NmzZql6OvrK7t27VLLAIqtra2ybNkyJSEhQbl06ZKybt06BVDi4uKUa9euKffv31cURVF++uknZd26dcqFCxeU48ePK35+fkqdOnWUzMxMrRzVqFFD2bJlixIXF6e88847irOzs5Kenq4oiqIcP35cMTIyUj7++GMlNjZWOXXqlPLll18qt27dUhRFUaZMmaLUqFFD2bZtm5KQkKBEREQoRkZGyp49e56aTyGKM2noCyGEKDYCAwMVfX19xczMTH288847iqIoStu2bZVp06Zplf/+++8Ve3v7p9b3448/KtbW1urziIgIxdLSMlc5XRv6c+fO1SpTpUoVZdWqVVrLJk+erHh6ej41prwa+u+//776/Nq1awqgTJgwQV128OBBBVCuXbumHgeg/P7772qZs2fPKoBy6NAhRVEUpVmzZkq/fv209v3uu+8qvr6+Wsc9bNgwrTK7d+9WAOXevXtPPQZFUZRbt24pgHLy5ElFUf4vR0uXLlXLnD59WgGUs2fPKoqiKD169FCaN2+eZ32PHz9WTE1NlQMHDmgt79Onj9KjR49nxiJEcSVj9IUQQhQrrVu3ZtGiRepzMzMzAE6cOEFMTAxTp05V12VmZvL48WMePXqEqakpO3bsIDw8nHPnzpGUlERGRobW+v+qYcOG6u8pKSkkJCTQp08f+vXrpy7PyMjA0tIyX/XWrVtX/b18+fIA1KlTJ9eymzdvYmdnB0CpUqVo1KiRWqZGjRqUKVOGs2fP0rhxY86ePUv//v219tO8eXPmzZv31GN6lgsXLhAaGsqhQ4e4ffs2WVlZACQmJlK7du08j8Xe3l6Nu0aNGsTGxvLuu+/mWX98fDyPHj2iXbt2WsvT0tLw8PDQKUYhihtp6AshhChWzMzMqFq1aq7lycnJTJo0iS5duuRaZ2xszKVLl+jUqRMff/wxU6dOxcrKiv3799OnTx/S0tKe2dDXaDQoiqK1LK8LU3O+dOTEA7BkyRKaNGmiVS7nmgJdPXlRr0ajeeqynMb1y/TkMT2Ln58fzs7OLFmyBAcHB7KysqhduzZpaWla5Z4Vt4mJyVPrz8nn1q1bqVChgtY6IyMjnWIUoriRhr4QQogSoX79+sTFxeX5JQDg2LFjZGVlMWvWLPT0sueqWLt2rVYZQ0NDMjMzc21rY2PDtWvX1OcXLlzg0aNHz4ynfPnyODg48OeffxIQEJDfw/nPMjIyOHr0KI0bNwYgLi6O+/fv4+bmBoCbmxsxMTEEBgaq28TExFCzZs1n1mtoaAiglac7d+4QFxfHkiVLaNmyJQD79+/Pd8x169Zl586dTJo0Kde6mjVrYmRkRGJiIq1atcp33UIUR9LQF0IIUSKEhobSqVMnnJyceOedd9DT0+PEiROcOnWKKVOmULVqVdLT0/nyyy/x8/MjJiaGr7/+WqsOFxcXkpOT2blzJ+7u7piammJqakqbNm1YsGABnp6eZGZmMmbMGJ2mzpw0aRJDhgzB0tISHx8fUlNTOXr0KPfu3WPEiBGvKhVAds/54MGDmT9/PqVKlSI4OJimTZuqDf/Ro0fTrVs3PDw88Pb25ueff2b9+vXs2LHjmfU6Ozuj0WjYsmULvr6+mJiYULZsWaytrfnmm2+wt7cnMTGRsWPH5jvmkJAQ6tSpw8CBAxkwYACGhobs3r2bd999l3LlyjFq1CiGDx9OVlYWLVq04MGDB8TExGBhYaH1hUWIkkKm1xRCCFEidOjQgS1btrB9+3YaNWpE06ZNmTNnDs7OzgC4u7sze/ZsPv/8c2rXrs3KlSsJDw/XqqNZs2YMGDCA7t27Y2Njw4wZMwCYNWsWjo6OtGzZkp49ezJq1CidxvT37duXpUuXEhERQZ06dWjVqhWRkZFUqlTp5SfgX0xNTRkzZgw9e/akefPmmJub88MPP6jr/f39mTdvHjNnzqRWrVosXryYiIgIvLy8nllvhQoVmDRpEmPHjqV8+fIEBwejp6fHmjVrOHbsGLVr12b48OF88cUX+Y65WrVqbN++nRMnTtC4cWM8PT3ZtGkTpUpl91tOnjyZCRMmEB4ejpubGz4+PmzdurVA8inE60ij/HtQoRBCCCGKtcjISIYNGyZ3rxWimJMefSGEEEIIIYohaegLIYQQQghRDMnQHSGEEEIIIYoh6dEXQgghhBCiGJKGvhBCiP/Xbh3IAAAAAAzyt77HVxQBMCT6AAAwJPoAADAk+gAAMCT6AAAwJPoAADAk+gAAMCT6AAAwFBpKGul/NEGPAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train data size: 2539543\n" - ] - } - ], "source": [ "print('train data size: ', len(train_data))\n", "\n", @@ -1489,22 +1444,66 @@ " 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: 2540320\n", + "feature_contri: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n", + "原始训练集大小: 2540320\n", + "划分后的训练集大小: 2259332, 验证集大小: 280988\n", + "feature_columns size: 84\n", + "Training until validation scores don't improve for 50 rounds\n", + "[100]\ttrain's ndcg@1: 0.795687\tvalid's ndcg@1: 0.550715\n", + "Early stopping, best iteration is:\n", + "[58]\ttrain's ndcg@1: 0.73593\tvalid's ndcg@1: 0.595665\n", + "Evaluated only: ndcg@1\n" + ] + }, + { + "data": { + "text/plain": [ + "
" + ], + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "
" + ], + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train data size: 2540320\n" + ] + } + ], + "execution_count": 16 }, { "cell_type": "code", - "execution_count": 16, "id": "1a248706-e58a-406f-9268-6dce3de2d863", "metadata": { - "ExecuteTime": { - "end_time": "2025-03-27T16:17:43.436380Z", - "start_time": "2025-03-27T16:17:43.431329Z" - }, "jupyter": { "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-31T14:59:05.601202Z", + "start_time": "2025-03-31T14:59:05.593686Z" } }, - "outputs": [], "source": [ "\n", "\n", @@ -1542,38 +1541,19 @@ "# catboost_params['feature_weights'] = feature_weights\n", "\n", "# # model, scaler = train_catboost(train_data, test_data.dropna(subset=['label']), feature_columns_new, catboost_params, plot=True)" - ] + ], + "outputs": [], + "execution_count": 17 }, { "cell_type": "code", - "execution_count": 17, "id": "5d1522a7538db91b", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:18:27.148839Z", - "start_time": "2025-03-27T16:17:43.570507Z" + "end_time": "2025-03-31T15:06:24.620370Z", + "start_time": "2025-03-31T15:05:42.433755Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "33.808630393996246\n", - " trade_date ts_code future_return future_score label\n", - "1018787 2023-01-03 603232.SH -0.048337 0.009385 25.0\n", - "861926 2023-01-04 601155.SH 0.011962 0.027340 43.0\n", - "56178 2023-01-05 000596.SZ 0.006810 0.025770 42.0\n", - "919818 2023-01-06 601865.SH -0.016155 0.009868 36.0\n", - "825775 2023-01-09 600961.SH -0.007557 0.016855 44.0\n", - "270858 2023-01-10 002238.SZ 0.137958 0.110154 49.0\n", - "440009 2023-01-11 002762.SZ 0.165666 0.126402 49.0\n", - "705317 2023-01-12 600556.SH 0.005312 0.028766 42.0\n", - "94754 2023-01-13 000786.SZ -0.011414 0.007786 28.0\n", - "179564 2023-01-16 001339.SZ -0.045814 -0.015564 3.0\n" - ] - } - ], "source": [ "# train_data = train_data.sort_values(by='trade_date')\n", "# all_dates = train_data['trade_date'].unique() # 获取所有唯一的 trade_date\n", @@ -1605,18 +1585,42 @@ "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": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "33.37918215613383\n", + " trade_date ts_code future_return future_score label\n", + "244753 2023-01-03 002148.SZ 0.015781 0.035495 45.0\n", + "876303 2023-01-04 601155.SH 0.011962 0.027340 43.0\n", + "57142 2023-01-05 000596.SZ 0.006810 0.025770 42.0\n", + "423377 2023-01-06 002683.SZ 0.030527 0.042989 47.0\n", + "615161 2023-01-09 600197.SH -0.047347 -0.005790 26.0\n", + "771090 2023-01-10 600729.SH -0.040330 -0.005321 23.0\n", + "734311 2023-01-11 600603.SH -0.037655 -0.000025 15.0\n", + "717317 2023-01-12 600556.SH 0.005312 0.028766 42.0\n", + "163236 2023-01-13 001236.SZ 0.240337 0.174003 49.0\n", + "182872 2023-01-16 001339.SZ -0.045814 -0.015564 3.0\n" + ] + } + ], + "execution_count": 23 }, { "cell_type": "code", - "execution_count": 18, "id": "d86af99d15cb3bdd", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:18:28.041081Z", - "start_time": "2025-03-27T16:18:27.264187Z" + "end_time": "2025-03-31T14:59:50.861647Z", + "start_time": "2025-03-31T14:59:50.140899Z" } }, + "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", @@ -1629,45 +1633,42 @@ "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", - "5090093 2025-03-17 603577.SH 20.05 20.20 0.011958\n", - "5093183 2025-03-18 603577.SH 20.27 20.07 0.015294\n", - "5096269 2025-03-19 603577.SH 20.31 20.27 0.017725\n", - "5099355 2025-03-20 603577.SH 20.58 20.31 NaN\n", - "5102442 2025-03-21 603577.SH 20.67 20.56 NaN\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", - "[1650 rows x 5 columns]\n" + "[1655 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']])" - ] + "execution_count": 19 }, { "cell_type": "code", - "execution_count": 19, "id": "ef9d068e-67f7-412c-bbd8-cdee7492dbc9", "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T16:18:28.111863Z", - "start_time": "2025-03-27T16:18:28.059714Z" + "end_time": "2025-03-31T14:59:51.110863Z", + "start_time": "2025-03-31T14:59:50.995929Z" } }, + "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.7431901412574308\n", - "0.6963809182594688\n" + "0.7433127289155628\n", + "0.6971824619501555\n" ] } ], - "source": [ - "print(train_data[\"future_score\"].corr(train_data[\"label\"]))\n", - "print(test_data[\"future_score\"].corr(test_data[\"label\"]))\n" - ] + "execution_count": 20 } ], "metadata": { diff --git a/code/train/Regression.ipynb b/code/train/Regression.ipynb new file mode 100644 index 0000000..dbb6b53 --- /dev/null +++ b/code/train/Regression.ipynb @@ -0,0 +1,1215 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "79a7758178bafdd3", + "metadata": {}, + "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": null + }, + { + "cell_type": "code", + "id": "a79cafb06a7e0e43", + "metadata": { + "jupyter": { + "source_hidden": 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", + "\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": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "f7a55c19-b7dc-4d2f-a478-cffab11690df", + "metadata": {}, + "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": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "4077d4449d406c86", + "metadata": {}, + "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": null + }, + { + "cell_type": "code", + "id": "c4e9e1d31da6dba6", + "metadata": {}, + "source": [ + "import numpy as np\n", + "import talib\n", + "\n", + "\n", + "def get_rolling_factor(df):\n", + " old_columns = df.columns.tolist()[:]\n", + " # 按股票和日期排序\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code', group_keys=False)\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['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['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['circ_mv']).reset_index(level=0, drop=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['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['circ_mv']\n", + "\n", + " df['mv_adjusted_volume'] = df['vol'] / df['circ_mv']\n", + "\n", + " df['mv_weighted_turnover'] = df['turnover_rate'] * (1 / df['circ_mv'])\n", + "\n", + " df['nonlinear_mv_volume'] = df['vol'] / df['circ_mv']\n", + "\n", + " df['mv_volume_ratio'] = df['volume_ratio'] / df['circ_mv']\n", + "\n", + " df['mv_momentum'] = df['turnover_rate'] * df['volume_ratio'] / df['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": null + }, + { + "cell_type": "code", + "id": "a735bc02ceb4d872", + "metadata": {}, + "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": null + }, + { + "cell_type": "code", + "id": "53f86ddc0677a6d7", + "metadata": { + "scrolled": true + }, + "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": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "dbe2fd8021b9417f", + "metadata": { + "scrolled": true + }, + "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", + " 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": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "d345bcc43b15579e", + "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", + " 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": null + }, + { + "cell_type": "code", + "id": "5f3d9aece75318cd", + "metadata": {}, + "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": null + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "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", + " volatility = stock_df['close'].pct_change().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", + "\n", + " return score\n", + "\n", + " scores = df.groupby('ts_code').apply(lambda x: compute_stock_score(x))\n", + " scores = scores.reset_index(level=0, drop=True)\n", + "\n", + " return scores\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", + "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", + " 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 gc\n", + "\n", + "gc.collect()" + ], + "id": "d4e2d1d432e56485" + }, + { + "cell_type": "code", + "id": "cf7de0b77db39655", + "metadata": {}, + "source": [ + "days = 2\n", + "validation_days = 120\n", + "\n", + "import gc\n", + "\n", + "gc.collect()\n", + "\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')['future_return']\n", + " .transform(lambda x: x.rolling(days).std())\n", + ")\n", + "\n", + "df['future_score'] = (\n", + " 0.7 * df['future_return'] +\n", + " 0.3 * df['future_volatility']\n", + ")\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=50, 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-01-01') & (df['trade_date'] >= '2000-01-01')]\n", + "test_data = df[filter_index & (df['trade_date'] >= '2023-01-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", + "\n", + " # avg_vol_3 = stock_df.groupby('ts_code')['vol'].rolling(window=3).mean().reset_index(level=0, drop=True)\n", + " # avg_vol_5 = stock_df.groupby('ts_code')['vol'].rolling(window=5).mean().shift(3).reset_index(level=0, drop=True)\n", + "\n", + " # stock_df = stock_df[\n", + " # (stock_df['cat_up_limit'] == 1) |\n", + " # (stock_df['vol'] > vol_spike_multiplier * stock_df['vol_spike'])\n", + " # ]\n", + " # cd1 = stock_df[\"close\"] > stock_df[\"close\"].shift(1)\n", + "\n", + " # cd2 = stock_df[\"close\"] > stock_df[\"close\"].rolling(window=10).mean()\n", + " #\n", + " # cd3 = (avg_vol_3 > avg_vol_5 * 2)\n", + " #\n", + " # cd4 = stock_df['gap_next_open'] < 0\n", + "\n", + " # stock_df = stock_df[(cd2 & cd4) | cd3]\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 = 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", + "feature_columns, _ = remove_shifted_features(train_data[train_data['label'] == train_data['label'].max()],\n", + " test_data[test_data['label'] == test_data['label'].max()],\n", + " feature_columns)\n", + "\n", + "feature_columns = remove_highly_correlated_features(train_data[train_data['label'] == train_data['label'].max()],\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.remove('cat_l2_code')" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "93fc58a33c6ff6d8", + "metadata": {}, + "source": [ + "# def remove_highly_correlated_features(df, feature_columns, threshold=0.8):\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", + "# return remaining_features\n", + "#\n", + "# feature_columns = remove_highly_correlated_features(train_data, feature_columns)\n", + "# keep_columns = [col for col in train_data.columns if col in feature_columns or col in ['ts_code', 'trade_date', 'label']]\n", + "# train_data = train_data[keep_columns]\n", + "# test_data = test_data[keep_columns]\n", + "# print(f'feature_columns size: {len(feature_columns)}')" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "8f134d435f71e9e2", + "metadata": { + "jupyter": { + "source_hidden": true + } + }, + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "import lightgbm as lgb\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from catboost import Pool\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):\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", + " categorical_feature = [i for i, col in enumerate(feature_columns) if col.startswith('cat')]\n", + " print(f'categorical_feature: {categorical_feature}')\n", + "\n", + " train_data = lgb.Dataset(X_train, label=y_train, categorical_feature=categorical_feature)\n", + " val_data = lgb.Dataset(X_val, label=y_val, categorical_feature=categorical_feature)\n", + " model = lgb.train(\n", + " params, train_data, num_boost_round=num_boost_round,\n", + " valid_sets=[train_data, val_data], valid_names=['train', 'valid'],\n", + " callbacks=callbacks\n", + " )\n", + "\n", + " if print_feature_importance:\n", + " lgb.plot_metric(evals)\n", + " # lgb.plot_tree(model, figsize=(20, 8))\n", + " lgb.plot_importance(model, importance_type='split', max_num_features=20)\n", + " plt.show()\n", + " return model, scaler\n", + "\n", + "\n", + "from catboost import CatBoostRegressor\n", + "import pandas as pd\n", + "\n", + "\n", + "def train_catboost(train_data_df, test_data_df, feature_columns, params=None, plot=False):\n", + " train_data_df, test_data_df = train_data_df.dropna(subset=['label']), test_data_df.dropna(subset=['label'])\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", + " cat_features = [i for i, col in enumerate(feature_columns) if col.startswith('cat')]\n", + " print(f'cat_features: {cat_features}')\n", + " train_pool = Pool(data=X_train, label=y_train, cat_features=cat_features)\n", + " val_pool = Pool(data=X_val, label=y_val, cat_features=cat_features)\n", + "\n", + " model = CatBoostRegressor(**params)\n", + " model.fit(train_pool,\n", + " eval_set=[train_pool, val_pool])\n", + "\n", + " return model" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "4a4542e1ed6afe7d", + "metadata": {}, + "source": [ + "\n", + "\n", + "light_params = {\n", + " 'objective': 'regression',\n", + " 'metric': 'l2',\n", + " # 'objective': 'quantile', # 分位回归\n", + " # 'metric': 'quantile', # 使用 quantile 作为评估指标\n", + " # 'alpha': 0.75, # 90% 分位数\n", + " 'learning_rate': 0.01,\n", + " 'is_unbalance': True,\n", + " 'num_leaves': 2048,\n", + " 'min_data_in_leaf': 128,\n", + " 'max_depth': 6,\n", + " 'max_bin': 1024,\n", + " 'feature_fraction': 0.7,\n", + " 'bagging_fraction': 0.7,\n", + " 'bagging_freq': 5,\n", + " 'lambda_l1': 1,\n", + " 'lambda_l2': 1,\n", + " # 'boosting_type': 'dart',\n", + " 'verbosity': -1,\n", + " 'seed': 7\n", + "}" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "beeb098799ecfa6a", + "metadata": {}, + "source": [ + "print('train data size: ', len(train_data))\n", + "\n", + "evals = {}\n", + "model, scaler = train_light_model(train_data, light_params, feature_columns,\n", + " [lgb.log_evaluation(period=500),\n", + " lgb.callback.record_evaluation(evals),\n", + " lgb.early_stopping(50, first_metric_only=True)\n", + " ], evals, validation_days=120,\n", + " num_boost_round=24, print_feature_importance=True)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "63235069-dc59-48fb-961a-e80373e41a61", + "metadata": {}, + "source": [ + "print('train data size: ', len(train_data))\n", + "\n", + "catboost_params = {\n", + " 'loss_function': 'MAE', # 90% 分位回归\n", + " 'iterations': 5000, # 训练轮数\n", + " 'learning_rate': 0.05, # 学习率,较低以防止过拟合\n", + " 'depth': 10, # 树的深度,防止过拟合\n", + " # 'l1_leaf_reg': 10.0, # l1 正则化,提高泛化能力\n", + " # 'bagging_temperature': 1, # 降低过拟合\n", + " # 'subsample': 0.8, # 每轮随机 80% 的样本,减少过拟合\n", + " 'colsample_bylevel': 0.8, # 每层 80% 特征子集,防止过拟合\n", + " 'random_seed': 42, # 固定随机种子,保证可复现\n", + " 'verbose': 500, # 每 100 轮打印一次信息\n", + " 'early_stopping_rounds': 100, # 早停,防止过拟合\n", + " # 'task_type': 'GPU'\n", + "}\n", + "\n", + "# model = train_catboost(train_data, test_data, feature_columns, catboost_params)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "e3ac761d8f0b5d31", + "metadata": {}, + "source": [ + "score_df = test_data.copy()\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['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)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "b427ce41-9739-4e9e-bea8-5f2551fec5d7", + "metadata": { + "jupyter": { + "source_hidden": true + } + }, + "source": [ + "print(score_df[['trade_date', 'ts_code', 'score', 'label']])\n", + "print(score_df['label'].mean())" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "8f9a2b7b-11fe-4eb5-aa11-c4066fe418a1", + "metadata": {}, + "source": [], + "outputs": [], + "execution_count": null + } + ], + "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/RollingRank.ipynb b/code/train/RollingRank.ipynb index 29e5326..daa3ce8 100644 --- a/code/train/RollingRank.ipynb +++ b/code/train/RollingRank.ipynb @@ -8,8 +8,8 @@ "source_hidden": true }, "ExecuteTime": { - "end_time": "2025-03-29T17:43:30.876671Z", - "start_time": "2025-03-29T17:43:30.425776Z" + "end_time": "2025-03-31T14:33:30.607252Z", + "start_time": "2025-03-31T14:33:30.170544Z" } }, "source": [ @@ -32,8 +32,8 @@ "metadata": { "scrolled": true, "ExecuteTime": { - "end_time": "2025-03-29T17:44:18.824363Z", - "start_time": "2025-03-29T17:43:30.876671Z" + "end_time": "2025-03-31T14:34:19.160370Z", + "start_time": "2025-03-31T14:33:30.794750Z" } }, "source": [ @@ -73,15 +73,11 @@ "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: 8450470 entries, 0 to 8450469\n", + "RangeIndex: 8477357 entries, 0 to 8477356\n", "Data columns (total 31 columns):\n", " # Column Dtype \n", "--- ------ ----- \n", @@ -132,8 +128,8 @@ "source_hidden": true }, "ExecuteTime": { - "end_time": "2025-03-29T17:44:28.421215Z", - "start_time": "2025-03-29T17:44:19.106345Z" + "end_time": "2025-03-31T14:34:30.996034Z", + "start_time": "2025-03-31T14:34:19.168375Z" } }, "source": [ @@ -200,8 +196,8 @@ "id": "c4e9e1d31da6dba6", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:44:28.620721Z", - "start_time": "2025-03-29T17:44:28.436697Z" + "end_time": "2025-03-31T14:34:31.276589Z", + "start_time": "2025-03-31T14:34:31.060910Z" } }, "source": [ @@ -292,8 +288,8 @@ "source_hidden": true }, "ExecuteTime": { - "end_time": "2025-03-29T17:44:28.706766Z", - "start_time": "2025-03-29T17:44:28.650141Z" + "end_time": "2025-03-31T14:34:31.348068Z", + "start_time": "2025-03-31T14:34:31.304847Z" } }, "source": [ @@ -609,8 +605,8 @@ }, "scrolled": true, "ExecuteTime": { - "end_time": "2025-03-29T17:44:33.959917Z", - "start_time": "2025-03-29T17:44:28.720764Z" + "end_time": "2025-03-31T14:34:36.714777Z", + "start_time": "2025-03-31T14:34:31.369443Z" } }, "source": [ @@ -668,8 +664,8 @@ "id": "dbe2fd8021b9417f", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:44:33.985360Z", - "start_time": "2025-03-29T17:44:33.975319Z" + "end_time": "2025-03-31T14:34:36.727797Z", + "start_time": "2025-03-31T14:34:36.724265Z" } }, "source": [ @@ -696,8 +692,8 @@ "id": "85c3e3d0235ffffa", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:46:27.764400Z", - "start_time": "2025-03-29T17:44:34.016244Z" + "end_time": "2025-03-31T14:37:04.071963Z", + "start_time": "2025-03-31T14:34:36.756415Z" } }, "source": [ @@ -736,7 +732,7 @@ "output_type": "stream", "text": [ "\n", - "Index: 5102787 entries, 0 to 5102786\n", + "Index: 5118212 entries, 0 to 5118211\n", "Columns: 115 entries, ts_code to mv_momentum\n", "dtypes: bool(12), datetime64[ns](1), float64(98), int32(1), int64(1), object(2)\n", "memory usage: 4.0+ GB\n", @@ -751,8 +747,8 @@ "id": "92d84ce15a562ec6", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:46:29.644814Z", - "start_time": "2025-03-29T17:46:28.384214Z" + "end_time": "2025-03-31T14:37:05.401297Z", + "start_time": "2025-03-31T14:37:04.287413Z" } }, "source": [ @@ -795,8 +791,8 @@ "source_hidden": true }, "ExecuteTime": { - "end_time": "2025-03-29T17:46:29.655148Z", - "start_time": "2025-03-29T17:46:29.646857Z" + "end_time": "2025-03-31T14:37:05.435586Z", + "start_time": "2025-03-31T14:37:05.429705Z" } }, "source": [ @@ -844,8 +840,8 @@ "id": "40e6b68a91b30c79", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:46:30.303881Z", - "start_time": "2025-03-29T17:46:29.698776Z" + "end_time": "2025-03-31T14:37:05.994210Z", + "start_time": "2025-03-31T14:37:05.479565Z" } }, "source": [ @@ -1013,8 +1009,8 @@ "id": "1c46817a-b5dd-4bec-8bb4-e6e80bfd9d66", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:46:30.320706Z", - "start_time": "2025-03-29T17:46:30.307889Z" + "end_time": "2025-03-31T14:37:06.026489Z", + "start_time": "2025-03-31T14:37:06.024035Z" } }, "source": "# print(test_data.head()[['act_factor1', 'act_factor2', 'ts_code', 'trade_date']])", @@ -1026,8 +1022,8 @@ "id": "da2bb202843d9275", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:46:30.929501Z", - "start_time": "2025-03-29T17:46:30.343347Z" + "end_time": "2025-03-31T14:37:06.597135Z", + "start_time": "2025-03-31T14:37:06.031495Z" } }, "source": [ @@ -1133,14 +1129,50 @@ "execution_count": 13 }, { - "cell_type": "code", - "id": "20b7836efae720a3", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:46:31.021203Z", - "start_time": "2025-03-29T17:46:30.953273Z" + "end_time": "2025-03-31T14:37:11.087120Z", + "start_time": "2025-03-31T14:37:06.619979Z" } }, + "cell_type": "code", + "source": [ + "\n", + "days = 2\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')['future_return']\n", + " .transform(lambda x: x.rolling(days).std())\n", + ")\n", + "\n", + "df['future_score'] = (\n", + " 0.7 * df['future_return'] +\n", + " 0.3 * df['future_volatility']\n", + ")\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", + "filter_index2 = df['future_return'].between(df['future_return'].quantile(0.01), df['future_return'].quantile(0.99))\n", + "filter_index2 = df['future_volatility'].between(df['future_volatility'].quantile(0.01),\n", + " df['future_volatility'].quantile(0.99)) | filter_index2\n", + "df['label'] = df.groupby('trade_date', group_keys=False)['future_score'].transform(\n", + " lambda x: pd.qcut(x, q=50, labels=False, duplicates='drop')\n", + ")\n" + ], + "id": "81d4570663ae21d7", + "outputs": [], + "execution_count": 14 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-31T14:39:16.314466Z", + "start_time": "2025-03-31T14:39:15.609756Z" + } + }, + "cell_type": "code", "source": [ "# print('train data size: ', len(train_data))\n", "\n", @@ -1170,54 +1202,20 @@ "\n", "gc.collect()" ], + "id": "92428d543f4727ad", "outputs": [ { "data": { "text/plain": [ - "0" + "6302" ] }, - "execution_count": 14, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 14 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-03-29T17:46:35.629560Z", - "start_time": "2025-03-29T17:46:31.046580Z" - } - }, - "cell_type": "code", - "source": [ - "\n", - "days = 2\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')['future_return']\n", - " .transform(lambda x: x.rolling(days).std())\n", - ")\n", - "\n", - "df['future_score'] = (\n", - " 0.7 * df['future_return'] +\n", - " 0.3 * df['future_volatility']\n", - ")\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_score'].transform(\n", - " lambda x: pd.qcut(x, q=50, labels=False, duplicates='drop')\n", - ")\n" - ], - "id": "81d4570663ae21d7", - "outputs": [], - "execution_count": 15 + "execution_count": 21 }, { "cell_type": "code", @@ -1227,8 +1225,8 @@ "source_hidden": true }, "ExecuteTime": { - "end_time": "2025-03-29T17:46:35.745784Z", - "start_time": "2025-03-29T17:46:35.675465Z" + "end_time": "2025-03-31T14:39:16.430821Z", + "start_time": "2025-03-31T14:39:16.321471Z" } }, "source": [ @@ -1255,7 +1253,7 @@ "\n", " # 根据日期筛选数据\n", " train_data = df[filter_index & df['trade_date'].isin(train_dates)]\n", - " test_data = df[filter_index & df['trade_date'].isin(test_dates)]\n", + " test_data = df[filter_index2 & 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", @@ -1403,15 +1401,15 @@ " return final_predictions\n" ], "outputs": [], - "execution_count": 16 + "execution_count": 22 }, { "cell_type": "code", "id": "63235069-dc59-48fb-961a-e80373e41a61", "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:52:27.309071Z", - "start_time": "2025-03-29T17:46:35.756531Z" + "end_time": "2025-03-31T14:45:27.262907Z", + "start_time": "2025-03-31T14:39:16.454548Z" } }, "source": [ @@ -1429,172 +1427,171 @@ "text": [ "去极值\n", "去极值\n", - "检测到 20 个可能漂移的特征: ['pct_chg', 'turnover_rate', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'act_factor3', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'log_close', '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', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2020-03-11\n", - "最大日期: 2022-03-30\n", - "最小日期: 2022-03-31\n", - "最大日期: 2022-06-30\n", - "原始训练集大小: 402534\n", - "划分后的训练集大小: 307694, 验证集大小: 94840\n", + "检测到 21 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'act_factor3', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'log_close', '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', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", + "最小日期: 2020-03-18\n", + "最大日期: 2022-04-08\n", + "最小日期: 2022-04-11\n", + "最大日期: 2022-07-07\n", + "原始训练集大小: 402509\n", + "划分后的训练集大小: 307874, 验证集大小: 94635\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[15]\ttrain's ndcg@1: 0.662414\tvalid's ndcg@1: 0.613612\n", + "[1]\ttrain's ndcg@1: 0.519951\tvalid's ndcg@1: 0.628242\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", "检测到 26 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_kurtosis', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'act_factor3', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'resonance_factor', 'log_close', 'obv-maobv_6', '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', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2020-06-09\n", - "最大日期: 2022-06-30\n", - "最小日期: 2022-07-01\n", - "最大日期: 2022-09-23\n", - "原始训练集大小: 400974\n", - "划分后的训练集大小: 306295, 验证集大小: 94679\n", + "最小日期: 2020-06-16\n", + "最大日期: 2022-07-07\n", + "最小日期: 2022-07-08\n", + "最大日期: 2022-09-30\n", + "原始训练集大小: 401052\n", + "划分后的训练集大小: 306109, 验证集大小: 94943\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[29]\ttrain's ndcg@1: 0.705891\tvalid's ndcg@1: 0.608979\n", + "[19]\ttrain's ndcg@1: 0.656354\tvalid's ndcg@1: 0.628231\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 14 个可能漂移的特征: ['turnover_rate', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'log(circ_mv)', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'log_close', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small']\n", - "最小日期: 2020-09-03\n", - "最大日期: 2022-09-23\n", - "最小日期: 2022-09-26\n", - "最大日期: 2022-12-23\n", - "原始训练集大小: 398352\n", - "划分后的训练集大小: 303767, 验证集大小: 94585\n", + "检测到 14 个可能漂移的特征: ['vol', 'turnover_rate', 'vol_spike', 'atr_14', 'atr_6', 'log(circ_mv)', 'alpha_22_improved', 'turnover_std', 'log_close', 'obv-maobv_6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'buy_elg_vol_minus_sell_elg_vol']\n", + "最小日期: 2020-09-10\n", + "最大日期: 2022-09-30\n", + "最小日期: 2022-10-10\n", + "最大日期: 2022-12-30\n", + "原始训练集大小: 398374\n", + "划分后的训练集大小: 303799, 验证集大小: 94575\n", "Training until validation scores don't improve for 50 rounds\n", - "[100]\ttrain's ndcg@1: 0.827415\tvalid's ndcg@1: 0.630578\n", "Early stopping, best iteration is:\n", - "[71]\ttrain's ndcg@1: 0.78919\tvalid's ndcg@1: 0.66253\n", + "[8]\ttrain's ndcg@1: 0.624175\tvalid's ndcg@1: 0.60186\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 18 个可能漂移的特征: ['vol', 'turnover_rate', 'return_kurtosis', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'log(circ_mv)', 'turnover_std', 'log_close', 'obv-maobv_6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'buy_elg_vol_minus_sell_elg_vol', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2020-12-04\n", - "最大日期: 2022-12-23\n", - "最小日期: 2022-12-26\n", - "最大日期: 2023-03-27\n", - "原始训练集大小: 395407\n", - "划分后的训练集大小: 305189, 验证集大小: 90218\n", + "检测到 17 个可能漂移的特征: ['vol', 'turnover_rate', 'return_kurtosis', 'vol_spike', 'atr_14', 'atr_6', 'act_factor3', 'log(circ_mv)', 'turnover_std', 'log_close', 'obv-maobv_6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'buy_elg_vol_minus_sell_elg_vol', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", + "最小日期: 2020-12-11\n", + "最大日期: 2022-12-30\n", + "最小日期: 2023-01-03\n", + "最大日期: 2023-04-03\n", + "原始训练集大小: 395305\n", + "划分后的训练集大小: 305409, 验证集大小: 89896\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[49]\ttrain's ndcg@1: 0.808651\tvalid's ndcg@1: 0.637922\n", + "[23]\ttrain's ndcg@1: 0.711527\tvalid's ndcg@1: 0.58914\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 19 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_skew', 'return_kurtosis', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'act_factor3', 'resonance_factor', 'obv-maobv_6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2021-03-08\n", - "最大日期: 2023-03-27\n", - "最小日期: 2023-03-28\n", - "最大日期: 2023-06-27\n", - "原始训练集大小: 393886\n", - "划分后的训练集大小: 303266, 验证集大小: 90620\n", + "检测到 18 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_kurtosis', 'vol_spike', 'obv', 'maobv_6', 'rsi_3', 'act_factor3', 'delta_cov', 'resonance_factor', 'obv-maobv_6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'buy_elg_vol_minus_sell_elg_vol', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", + "最小日期: 2021-03-15\n", + "最大日期: 2023-04-03\n", + "最小日期: 2023-04-04\n", + "最大日期: 2023-07-04\n", + "原始训练集大小: 394279\n", + "划分后的训练集大小: 303561, 验证集大小: 90718\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[8]\ttrain's ndcg@1: 0.622492\tvalid's ndcg@1: 0.606022\n", + "[1]\ttrain's ndcg@1: 0.570726\tvalid's ndcg@1: 0.62316\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 23 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_skew', 'return_kurtosis', 'vol_spike', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'act_factor3', 'log(circ_mv)', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'resonance_factor', 'log_close', 'obv-maobv_6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2021-06-04\n", - "最大日期: 2023-06-27\n", - "最小日期: 2023-06-28\n", - "最大日期: 2023-09-19\n", - "原始训练集大小: 393201\n", - "划分后的训练集大小: 300541, 验证集大小: 92660\n", + "检测到 25 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_skew', 'return_kurtosis', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'act_factor2', 'act_factor3', 'log(circ_mv)', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'resonance_factor', 'log_close', 'obv-maobv_6', 'active_buy_volume_large', 'active_buy_volume_big', 'active_buy_volume_small', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", + "最小日期: 2021-06-11\n", + "最大日期: 2023-07-04\n", + "最小日期: 2023-07-05\n", + "最大日期: 2023-09-26\n", + "原始训练集大小: 392902\n", + "划分后的训练集大小: 300091, 验证集大小: 92811\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[3]\ttrain's ndcg@1: 0.623432\tvalid's ndcg@1: 0.604043\n", + "[4]\ttrain's ndcg@1: 0.623501\tvalid's ndcg@1: 0.608889\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 14 个可能漂移的特征: ['vol', 'pct_chg', 'return_kurtosis', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'act_factor3', 'log(circ_mv)', 'cov', 'alpha_22_improved', 'resonance_factor', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2021-08-30\n", - "最大日期: 2023-09-19\n", - "最小日期: 2023-09-20\n", - "最大日期: 2023-12-20\n", - "原始训练集大小: 386612\n", - "划分后的训练集大小: 296498, 验证集大小: 90114\n", + "检测到 11 个可能漂移的特征: ['pct_chg', 'return_skew', 'return_kurtosis', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'act_factor3', 'delta_cov', 'alpha_22_improved', 'resonance_factor']\n", + "最小日期: 2021-09-06\n", + "最大日期: 2023-09-26\n", + "最小日期: 2023-09-27\n", + "最大日期: 2023-12-27\n", + "原始训练集大小: 386164\n", + "划分后的训练集大小: 296301, 验证集大小: 89863\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[12]\ttrain's ndcg@1: 0.663139\tvalid's ndcg@1: 0.652339\n", + "[38]\ttrain's ndcg@1: 0.76403\tvalid's ndcg@1: 0.675597\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 14 个可能漂移的特征: ['pct_chg', 'turnover_rate', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'act_factor3', 'log(circ_mv)', 'cov', 'alpha_22_improved', 'turnover_std', 'resonance_factor', 'log_close']\n", - "最小日期: 2021-12-01\n", - "最大日期: 2023-12-20\n", - "最小日期: 2023-12-21\n", - "最大日期: 2024-03-22\n", - "原始训练集大小: 379352\n", - "划分后的训练集大小: 293416, 验证集大小: 85936\n", + "检测到 14 个可能漂移的特征: ['pct_chg', 'turnover_rate', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'act_factor3', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'log_close']\n", + "最小日期: 2021-12-08\n", + "最大日期: 2023-12-27\n", + "最小日期: 2023-12-28\n", + "最大日期: 2024-03-29\n", + "原始训练集大小: 379125\n", + "划分后的训练集大小: 293170, 验证集大小: 85955\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[5]\ttrain's ndcg@1: 0.634019\tvalid's ndcg@1: 0.638831\n", + "[17]\ttrain's ndcg@1: 0.707121\tvalid's ndcg@1: 0.6345\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 12 个可能漂移的特征: ['vol', 'turnover_rate', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'log(circ_mv)', 'cov', 'log_close', 'obv-maobv_6', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2022-03-03\n", - "最大日期: 2024-03-22\n", - "最小日期: 2024-03-25\n", - "最大日期: 2024-06-24\n", - "原始训练集大小: 379932\n", - "划分后的训练集大小: 290249, 验证集大小: 89683\n", + "检测到 13 个可能漂移的特征: ['vol', 'turnover_rate', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'log(circ_mv)', 'turnover_std', 'log_close', 'obv-maobv_6', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", + "最小日期: 2022-03-10\n", + "最大日期: 2024-03-29\n", + "最小日期: 2024-04-01\n", + "最大日期: 2024-07-01\n", + "原始训练集大小: 379627\n", + "划分后的训练集大小: 290158, 验证集大小: 89469\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[34]\ttrain's ndcg@1: 0.759349\tvalid's ndcg@1: 0.64614\n", + "[44]\ttrain's ndcg@1: 0.776797\tvalid's ndcg@1: 0.610802\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 18 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'vol_spike', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'act_factor3', 'cov', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'resonance_factor', 'log_close', 'obv-maobv_6', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2022-06-02\n", - "最大日期: 2024-06-24\n", - "最小日期: 2024-06-25\n", - "最大日期: 2024-09-18\n", - "原始训练集大小: 381303\n", - "划分后的训练集大小: 284860, 验证集大小: 96443\n", + "检测到 18 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_kurtosis', 'vol_spike', 'atr_6', 'maobv_6', 'rsi_3', 'act_factor3', 'cov', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'resonance_factor', 'log_close', 'obv-maobv_6', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", + "最小日期: 2022-06-10\n", + "最大日期: 2024-07-01\n", + "最小日期: 2024-07-02\n", + "最大日期: 2024-09-25\n", + "原始训练集大小: 381403\n", + "划分后的训练集大小: 284687, 验证集大小: 96716\n", "Training until validation scores don't improve for 50 rounds\n", "Early stopping, best iteration is:\n", - "[40]\ttrain's ndcg@1: 0.745108\tvalid's ndcg@1: 0.589855\n", + "[49]\ttrain's ndcg@1: 0.787077\tvalid's ndcg@1: 0.579916\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 21 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_kurtosis', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'act_factor1', 'act_factor3', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'resonance_factor', 'log_close', 'obv-maobv_6', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2022-08-26\n", - "最大日期: 2024-09-18\n", - "最小日期: 2024-09-19\n", - "最大日期: 2024-12-18\n", - "原始训练集大小: 379192\n", - "划分后的训练集大小: 285332, 验证集大小: 93860\n", + "检测到 20 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'vol_spike', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'rsi_3', 'act_factor3', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'turnover_std', 'resonance_factor', 'log_close', 'obv-maobv_6', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", + "最小日期: 2022-09-02\n", + "最大日期: 2024-09-25\n", + "最小日期: 2024-09-26\n", + "最大日期: 2024-12-25\n", + "原始训练集大小: 378304\n", + "划分后的训练集大小: 284976, 验证集大小: 93328\n", "Training until validation scores don't improve for 50 rounds\n", + "[100]\ttrain's ndcg@1: 0.840198\tvalid's ndcg@1: 0.629064\n", "Early stopping, best iteration is:\n", - "[10]\ttrain's ndcg@1: 0.629259\tvalid's ndcg@1: 0.646921\n", + "[98]\ttrain's ndcg@1: 0.836808\tvalid's ndcg@1: 0.644301\n", "Evaluated only: ndcg@1\n", "去极值\n", "去极值\n", - "检测到 18 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_skew', 'return_kurtosis', 'obv', 'maobv_6', 'rsi_3', 'act_factor1', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'resonance_factor', 'log_close', 'obv-maobv_6', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", - "最小日期: 2022-11-28\n", - "最大日期: 2024-12-18\n", - "最小日期: 2024-12-19\n", - "最大日期: 2025-03-19\n", - "原始训练集大小: 371676\n", - "划分后的训练集大小: 289037, 验证集大小: 82639\n", + "检测到 17 个可能漂移的特征: ['vol', 'pct_chg', 'turnover_rate', 'return_skew', 'obv', 'maobv_6', 'rsi_3', 'act_factor1', 'log(circ_mv)', 'cov', 'delta_cov', 'alpha_22_improved', 'resonance_factor', 'log_close', 'obv-maobv_6', 'mv_adjusted_volume', 'nonlinear_mv_volume']\n", + "最小日期: 2022-12-05\n", + "最大日期: 2024-12-25\n", + "最小日期: 2024-12-26\n", + "最大日期: 2025-03-26\n", + "原始训练集大小: 371843\n", + "划分后的训练集大小: 289769, 验证集大小: 82074\n", "Training until validation scores don't improve for 50 rounds\n", - "[100]\ttrain's ndcg@1: 0.844744\tvalid's ndcg@1: 0.580519\n", "Early stopping, best iteration is:\n", - "[54]\ttrain's ndcg@1: 0.792906\tvalid's ndcg@1: 0.599084\n", + "[2]\ttrain's ndcg@1: 0.592644\tvalid's ndcg@1: 0.596307\n", "Evaluated only: ndcg@1\n" ] } ], - "execution_count": 17 + "execution_count": 23 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-29T17:52:27.393208Z", - "start_time": "2025-03-29T17:52:27.388403Z" + "end_time": "2025-03-31T14:45:27.345941Z", + "start_time": "2025-03-31T14:45:27.342565Z" } }, "cell_type": "code", @@ -1609,208 +1606,17 @@ ] } ], - "execution_count": 18 - }, - { - "cell_type": "code", - "id": "d86af99d15cb3bdd", - "metadata": { - "scrolled": true - }, - "source": [ - "import pandas as pd\n", - "\n", - "gc.collect()\n", - "def rolling_train_predict(df, train_days, test_days, industry_df, index_df, days=5, use_pca=False, validation_days=60):\n", - "\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", - "\n", - " for start in range(start_index, n - train_days - test_days + 1, test_days):\n", - " gc.collect()\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", - " 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", - " \n", - " def select_pre_zt_stocks_dynamic(\n", - " stock_df,\n", - " vol_spike_multiplier=1.5,\n", - " min_return=0.03, # 最小累计涨幅(例如 3%)\n", - " min_main_net_inflow=1e6, # 最小主力资金净流入(例如 100 万元)\n", - " window=30, # 计算历史均值的窗口大小\n", - " signal_days=1 # 异动信号需要连续出现的天数\n", - " ):\n", - " \n", - " # 排序数据\n", - " stock_df = stock_df.sort_values(by=['trade_date', 'ts_code'])\n", - " \n", - " # stock_df = stock_df[\n", - " # (stock_df['vol'] > vol_spike_multiplier * stock_df['avg_vol_20'])\n", - " # ]\n", - " cd1 = stock_df[\"close\"] > stock_df[\"close\"].shift(1)\n", - "\n", - " cd2 = stock_df[\"close\"] > stock_df[\"close\"].rolling(window=10).mean()\n", - "\n", - " cd3 = (stock_df[\"vol\"] > stock_df[\"vol\"].shift(1)) & (stock_df[\"vol\"] < 10 * stock_df[\"vol\"].shift(1))\n", - "\n", - " stock_df = stock_df[cd1 & cd2 & cd3]\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", - " train_data = select_pre_zt_stocks_dynamic(train_data)\n", - " test_data = select_pre_zt_stocks_dynamic(test_data)\n", - "\n", - " \n", - " # train_data, _ = get_simple_factor(train_data)\n", - " # test_data, _ = get_simple_factor(test_data)\n", - "\n", - " # df['future_return'] = (df.groupby('ts_code')['close'].shift(-days) - df.groupby('ts_code')['open'].shift(-1)) / \\\n", - " # df.groupby('ts_code')['open'].shift(-1)\n", - " \n", - " def symmetric_log_transform(values):\n", - " return np.sign(values) * np.log1p(np.abs(values))\n", - "\n", - " train_data['future_return'] = train_data.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1)\n", - " train_data['future_score'] = calculate_score(train_data, days=days, lambda_param=0.3)\n", - " # train_data['future_score'] = symmetric_log_transform(train_data['future_score'])\n", - "\n", - " test_data['future_return'] = test_data.groupby('ts_code', group_keys=False)['close'].apply(lambda x: x.shift(-days) / x - 1)\n", - " test_data['future_score'] = calculate_score(test_data, days=days, lambda_param=0.3)\n", - " # test_data['future_score'] = symmetric_log_transform(test_data['future_score'])\n", - " \n", - " train_data['label'] = train_data.groupby('trade_date', group_keys=False)['future_score'].transform(\n", - " lambda x: pd.qcut(x, q=10, 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=10, labels=False, duplicates='drop')\n", - " )\n", - " \n", - " industry_df = industry_df.sort_values(by=['trade_date'])\n", - " index_df = index_df.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_df, 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_df, 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 = [col for col in train_data.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 'score' 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(feature_columns)\n", - "\n", - " feature_columns_o = feature_columns[:]\n", - " train_data, feature_columns = create_deviation_within_dates(train_data, feature_columns_o)\n", - " test_data, _ = create_deviation_within_dates(test_data, feature_columns_o)\n", - " print(f'feature_columns size: {len(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_new)\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 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", - "\n", - " feature_columns = remove_highly_correlated_features(train_data[train_data['label'] == 9], feature_columns)\n", - " feature_columns, _ = remove_shifted_features(train_data[train_data['label'] == 9], test_data[test_data['label'] == 9], 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', 'future_score']]\n", - " train_data = train_data[keep_columns]\n", - "\n", - " label_gain = list(range(len(train_data['label'].unique())))\n", - " label_gain = [gain * 2 for gain in label_gain]\n", - " light_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", - " light_params['weight'] = train_data[\"trade_date\"].map(date_weights).tolist()\n", - "\n", - " print(f'feature_columns: {feature_columns}')\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(50, first_metric_only=True)\n", - " ], evals,\n", - " num_boost_round=3000, validation_days=validation_days,\n", - " print_feature_importance=False, use_pca=False)\n", - "\n", - " score_df = test_data.copy()\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", - " if use_pca and pca is not None:\n", - " score_df.loc[:, numeric_columns] = pca.transform(score_df[numeric_columns])\n", - " score_df['score'] = model.predict(score_df[feature_columns])\n", - " # train_data['score'] = catboost_model.predict(train_data[feature_columns_new])\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 = score_df[['trade_date', 'score', 'ts_code']]\n", - " predictions_list.append(score_df)\n", - " final_predictions = pd.concat(predictions_list, ignore_index=True)\n", - " return final_predictions\n", - "\n", - "\n", - "final_predictions = rolling_train_predict(df.sort_values(['trade_date'], ascending=[True]), 500, 60, industry_df, index_df, days=5, validation_days=100)\n", - "final_predictions.to_csv('predictions_test.tsv', index=False)\n" - ], - "outputs": [], - "execution_count": null - }, - { - "cell_type": "code", - "id": "7ed645f2-7755-496e-8a6d-c64adc9080ac", - "metadata": {}, - "source": [ - "print('finish')" - ], - "outputs": [], - "execution_count": null + "execution_count": 24 }, { "cell_type": "code", "id": "0dc75517-c857-4f1d-8815-e807400a6d33", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-31T14:45:27.395705Z", + "start_time": "2025-03-31T14:45:27.393319Z" + } + }, "source": [], "outputs": [], "execution_count": null diff --git a/code/train/UpdateSGD.ipynb b/code/train/UpdateSGD.ipynb new file mode 100644 index 0000000..23daaa2 --- /dev/null +++ b/code/train/UpdateSGD.ipynb @@ -0,0 +1,1393 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "79a7758178bafdd3", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T12:44:04.673079Z", + "start_time": "2025-03-02T12:44:04.247257Z" + } + }, + "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": { + "jupyter": { + "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-02T12:44:43.595370Z", + "start_time": "2025-03-02T12:44:04.688084Z" + } + }, + "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'],\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(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", + "\n", + "RangeIndex: 8369855 entries, 0 to 8369854\n", + "Data columns (total 21 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 turnover_rate float64 \n", + " 8 pe_ttm float64 \n", + " 9 circ_mv float64 \n", + " 10 volume_ratio float64 \n", + " 11 is_st bool \n", + " 12 up_limit float64 \n", + " 13 down_limit float64 \n", + " 14 buy_sm_vol float64 \n", + " 15 sell_sm_vol float64 \n", + " 16 buy_lg_vol float64 \n", + " 17 sell_lg_vol float64 \n", + " 18 buy_elg_vol float64 \n", + " 19 sell_elg_vol float64 \n", + " 20 net_mf_vol float64 \n", + "dtypes: bool(1), datetime64[ns](1), float64(18), object(1)\n", + "memory usage: 1.3+ GB\n", + "None\n" + ] + } + ], + "execution_count": 2 + }, + { + "cell_type": "code", + "id": "f7a55c19-b7dc-4d2f-a478-cffab11690df", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T12:44:46.323145Z", + "start_time": "2025-03-02T12:44:43.776850Z" + } + }, + "source": [ + "print('industry')\n", + "df = read_and_merge_h5_data('../../data/industry_data.h5', key='industry_data',\n", + " columns=['ts_code', 'l2_code'],\n", + " df=df, on=['ts_code'], join='left')\n" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "industry\n", + "left merge on ['ts_code']\n" + ] + } + ], + "execution_count": 3 + }, + { + "cell_type": "code", + "id": "4077d4449d406c86", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T12:44:46.389069Z", + "start_time": "2025-03-02T12:44:46.332410Z" + } + }, + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "\n", + "def calculate_indicators(df):\n", + " \"\"\"\n", + " 计算四个指标:当日涨跌幅、5日移动平均、RSI、MACD。\n", + " \"\"\"\n", + " df = df.sort_values('trade_date')\n", + " df['daily_return'] = (df['close'] - df['pre_close']) / df['pre_close'] * 100\n", + " # df['5_day_ma'] = df['close'].rolling(window=5).mean()\n", + " delta = df['close'].diff()\n", + " gain = delta.where(delta > 0, 0)\n", + " loss = -delta.where(delta < 0, 0)\n", + " avg_gain = gain.rolling(window=14).mean()\n", + " avg_loss = loss.rolling(window=14).mean()\n", + " rs = avg_gain / avg_loss\n", + " df['RSI'] = 100 - (100 / (1 + rs))\n", + "\n", + " # 计算MACD\n", + " ema12 = df['close'].ewm(span=12, adjust=False).mean()\n", + " ema26 = df['close'].ewm(span=26, adjust=False).mean()\n", + " df['MACD'] = ema12 - ema26\n", + " df['Signal_line'] = df['MACD'].ewm(span=9, adjust=False).mean()\n", + " df['MACD_hist'] = df['MACD'] - df['Signal_line']\n", + "\n", + " 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', 'MACD_hist'],\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": "c4e9e1d31da6dba6", + "metadata": { + "jupyter": { + "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-02T12:44:46.438183Z", + "start_time": "2025-03-02T12:44:46.409533Z" + } + }, + "source": [ + "import numpy as np\n", + "import talib\n", + "\n", + "\n", + "def get_technical_factor(df):\n", + " # 按股票和日期排序\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code', group_keys=False)\n", + "\n", + " # 计算 up 和 down\n", + " df['log_close'] = np.log(df['close'])\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", + " # 计算 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", + " df['obv-maobv_6'] = df['obv'] - df['maobv_6']\n", + "\n", + " # 计算 RSI\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", + " # 计算 avg_close_5\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", + " # 计算比值指标\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", + " return df\n", + "\n", + "\n", + "def get_act_factor(df, cat=True):\n", + " # 按股票和日期排序\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code', group_keys=False)\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", + " if cat:\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", + " # 根据 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", + " return df\n", + "\n", + "\n", + "def get_money_flow_factor(df):\n", + " # 计算资金流相关因子(字段名称见 tushare 数据说明)\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", + " return df\n", + "\n", + "\n", + "def get_alpha_factor(df):\n", + " df = df.sort_values(by=['ts_code', 'trade_date'])\n", + " grouped = df.groupby('ts_code')\n", + "\n", + " # alpha_022: 当前 close 与 5 日前 close 差值\n", + " df['alpha_022'] = grouped['close'].transform(lambda x: x - x.shift(5))\n", + "\n", + " # alpha_003: (close - open) / (high - low)\n", + " df['alpha_003'] = np.where(df['high'] != df['low'],\n", + " (df['close'] - df['open']) / (df['high'] - df['low']),\n", + " 0)\n", + "\n", + " # alpha_007: 计算过去5日 close 与 vol 的相关性,并按 trade_date 排名\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", + " # alpha_013: 计算过去5日 close 之和 - 20日 close 之和,并按 trade_date 排名\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", + " return df\n", + "\n" + ], + "outputs": [], + "execution_count": 5 + }, + { + "cell_type": "code", + "id": "a735bc02ceb4d872", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T12:44:54.093568Z", + "start_time": "2025-03-02T12:44:46.451967Z" + } + }, + "source": [ + "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.median())\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 = 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": "53f86ddc0677a6d7", + "metadata": { + "scrolled": true, + "ExecuteTime": { + "end_time": "2025-03-02T12:44:54.102298Z", + "start_time": "2025-03-02T12:44:54.093568Z" + } + }, + "source": [ + "origin_columns = df.columns.tolist()\n", + "origin_columns = [col for col in origin_columns if col not in ['turnover_rate', 'pe_ttm', 'volume_ratio', 'l2_code']]\n", + "origin_columns = [col for col in origin_columns if col not in index_data.columns]\n" + ], + "outputs": [], + "execution_count": 7 + }, + { + "cell_type": "code", + "id": "dbe2fd8021b9417f", + "metadata": { + "scrolled": true, + "ExecuteTime": { + "end_time": "2025-03-02T12:46:07.996377Z", + "start_time": "2025-03-02T12:44:54.115006Z" + } + }, + "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.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 = 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: 5732462 entries, 1964 to 5732460\n", + "Data columns (total 72 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 turnover_rate float64 \n", + " 8 pe_ttm float64 \n", + " 9 circ_mv float64 \n", + " 10 volume_ratio float64 \n", + " 11 is_st bool \n", + " 12 up_limit float64 \n", + " 13 down_limit float64 \n", + " 14 buy_sm_vol float64 \n", + " 15 sell_sm_vol float64 \n", + " 16 buy_lg_vol float64 \n", + " 17 sell_lg_vol float64 \n", + " 18 buy_elg_vol float64 \n", + " 19 sell_elg_vol float64 \n", + " 20 net_mf_vol float64 \n", + " 21 cat_l2_code object \n", + " 22 log_close float64 \n", + " 23 up float64 \n", + " 24 down float64 \n", + " 25 atr_14 float64 \n", + " 26 atr_6 float64 \n", + " 27 obv float64 \n", + " 28 maobv_6 float64 \n", + " 29 obv-maobv_6 float64 \n", + " 30 rsi_3 float64 \n", + " 31 rsi_6 float64 \n", + " 32 rsi_9 float64 \n", + " 33 return_5 float64 \n", + " 34 return_10 float64 \n", + " 35 return_20 float64 \n", + " 36 avg_close_5 float64 \n", + " 37 std_return_5 float64 \n", + " 38 std_return_15 float64 \n", + " 39 std_return_25 float64 \n", + " 40 std_return_90 float64 \n", + " 41 std_return_90_2 float64 \n", + " 42 std_return_5 / std_return_90 float64 \n", + " 43 std_return_5 / std_return_25 float64 \n", + " 44 std_return_90 - std_return_90_2 float64 \n", + " 45 ema_5 float64 \n", + " 46 ema_13 float64 \n", + " 47 ema_20 float64 \n", + " 48 ema_60 float64 \n", + " 49 act_factor1 float64 \n", + " 50 act_factor2 float64 \n", + " 51 act_factor3 float64 \n", + " 52 act_factor4 float64 \n", + " 53 cat_af1 bool \n", + " 54 cat_af2 bool \n", + " 55 cat_af3 bool \n", + " 56 cat_af4 bool \n", + " 57 act_factor5 float64 \n", + " 58 act_factor6 float64 \n", + " 59 rank_act_factor1 float64 \n", + " 60 rank_act_factor2 float64 \n", + " 61 rank_act_factor3 float64 \n", + " 62 active_buy_volume_large float64 \n", + " 63 active_buy_volume_big float64 \n", + " 64 active_buy_volume_small float64 \n", + " 65 buy_lg_vol_minus_sell_lg_vol float64 \n", + " 66 buy_elg_vol_minus_sell_elg_vol float64 \n", + " 67 log(circ_mv) float64 \n", + " 68 alpha_022 float64 \n", + " 69 alpha_003 float64 \n", + " 70 alpha_007 float64 \n", + " 71 alpha_013 float64 \n", + "dtypes: bool(5), datetime64[ns](1), float64(64), object(2)\n", + "memory usage: 2.9+ GB\n", + "None\n" + ] + } + ], + "execution_count": 8 + }, + { + "cell_type": "code", + "id": "d345bcc43b15579e", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T12:46:08.743665Z", + "start_time": "2025-03-02T12:46:08.728004Z" + } + }, + "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", + " num_features = [col for col in feature_columns if 'cat' not in col and 'industry' not in col]\n", + "\n", + " # 遍历所有数值型特征\n", + " for feature in num_features:\n", + " if feature == 'trade_date': # 不需要对 'trade_date' 计算偏差\n", + " continue\n", + "\n", + " # grouped_median = df.groupby(['trade_date', groupby_col])[feature].transform('median')\n", + " # deviation_col_name = f'deviation_median_{feature}'\n", + " # new_columns[deviation_col_name] = df[feature] - grouped_median\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": 9 + }, + { + "cell_type": "code", + "id": "5f3d9aece75318cd", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T12:46:08.949623Z", + "start_time": "2025-03-02T12:46:08.931989Z" + } + }, + "source": [ + "def get_qcuts(series, quantiles):\n", + " q = pd.qcut(series, q=quantiles, labels=False, duplicates='drop')\n", + " return q[-1] # 返回窗口最后一个元素的分位数标签\n", + "\n", + "\n", + "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_return'] = (df['future_close'] - df['close']) / df['close']\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", + " df['sharpe_ratio'] = df['future_return'] * df['future_volatility']\n", + " df['sharpe_ratio'].replace([np.inf, -np.inf], np.nan, inplace=True)\n", + "\n", + " return df['sharpe_ratio']\n", + "\n", + "\n" + ], + "outputs": [], + "execution_count": 10 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T13:16:09.082156Z", + "start_time": "2025-03-02T13:14:58.041672Z" + } + }, + "cell_type": "code", + "source": [ + "days = 3\n", + "future_close = df.groupby('ts_code')['close'].shift(-days)\n", + "future_return = (future_close - df['close']) / df['close']\n", + "df['label'] = future_return\n", + "\n", + "# df['label'] = remove_outliers_label_percentile(df['label'])\n", + "\n", + "# df = df.apply(lambda x: x.astype('float32') if x.dtype in ['float64', 'float32'] else x)\n", + "df = df.sort_values(by=['trade_date', 'ts_code'])\n", + "train_data = df[(df['trade_date'] <= '2023-01-01') & (df['trade_date'] >= '2016-01-01')]\n", + "test_data = df[(df['trade_date'] >= '2023-01-01')]\n", + "\n", + "train_data = train_data.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nsmallest(3000, 'log(circ_mv)')\n", + ")\n", + "test_data = test_data.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nsmallest(3000, 'log(circ_mv)')\n", + ")\n", + "train_data = train_data.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nlargest(1000, 'return_20')\n", + ")\n", + "test_data = test_data.groupby('trade_date', group_keys=False).apply(\n", + " lambda x: x.nlargest(1000, 'return_20')\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 = [col for col in train_data.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 'score' 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(feature_columns)\n", + "\n", + "feature_columns_new = feature_columns[:]\n", + "train_data, feature_columns_new = create_deviation_within_dates(train_data, feature_columns)\n", + "print(f'feature_columns size: {len(feature_columns_new)}')\n", + "test_data, feature_columns_new = create_deviation_within_dates(test_data, feature_columns)\n", + "print(f'feature_columns size: {len(feature_columns_new)}')\n", + "\n", + "train_data = train_data.dropna(subset=feature_columns_new)\n", + "train_data = train_data.dropna(subset=['label'])\n", + "train_data['label'] = remove_outliers_label_percentile(train_data['label'])\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_new)\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 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')" + ], + "id": "cf7de0b77db39655", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['turnover_rate', 'pe_ttm', 'volume_ratio', 'cat_l2_code', 'log_close', 'up', 'down', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'obv-maobv_6', 'rsi_3', 'rsi_6', 'rsi_9', 'return_5', 'return_10', 'return_20', 'avg_close_5', 'std_return_5', 'std_return_15', 'std_return_25', 'std_return_90', 'std_return_90_2', 'std_return_5 / std_return_90', 'std_return_5 / std_return_25', 'std_return_90 - std_return_90_2', 'ema_5', 'ema_13', 'ema_20', 'ema_60', 'act_factor1', 'act_factor2', 'act_factor3', 'act_factor4', 'cat_af1', 'cat_af2', 'cat_af3', 'cat_af4', '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', 'buy_lg_vol_minus_sell_lg_vol', 'buy_elg_vol_minus_sell_elg_vol', 'log(circ_mv)', 'alpha_022', 'alpha_003', 'alpha_007', 'alpha_013', '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_obv_deviation', 'industry_return_5_deviation', 'industry_return_20_deviation', 'industry_act_factor1_deviation', 'industry_act_factor2_deviation', 'industry_act_factor3_deviation', 'industry_act_factor4_deviation', 'industry_return_5_percentile', '000852.SH_MACD', '000905.SH_MACD', '399006.SZ_MACD', '000852.SH_MACD_hist', '000905.SH_MACD_hist', '399006.SZ_MACD_hist', '000852.SH_RSI', '000905.SH_RSI', '399006.SZ_RSI', '000852.SH_Signal_line', '000905.SH_Signal_line', '399006.SZ_Signal_line', '000852.SH_daily_return', '000905.SH_daily_return', '399006.SZ_daily_return']\n", + "feature_columns size: 157\n", + "feature_columns size: 157\n", + "Removed 23208 outliers.\n", + "1137173\n", + "最小日期: 2017-04-06\n", + "最大日期: 2022-12-30\n", + "396093\n", + "最小日期: 2023-01-03\n", + "最大日期: 2025-02-28\n" + ] + } + ], + "execution_count": 68 + }, + { + "cell_type": "code", + "id": "8f134d435f71e9e2", + "metadata": { + "jupyter": { + "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-02T13:23:29.071382Z", + "start_time": "2025-03-02T13:23:29.059163Z" + } + }, + "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", + "import numpy as np\n", + "\n", + "import pandas as pd\n", + "from sklearn.linear_model import SGDRegressor, LinearRegression\n", + "from sklearn.preprocessing import StandardScaler, OneHotEncoder\n", + "\n", + "\n", + "def train_sgd_model(train_data: pd.DataFrame,\n", + " feature_columns: list,\n", + " params: dict, print_feature_importance=True):\n", + " # Initialize scaler and encoder\n", + " scaler = StandardScaler()\n", + " encoder = OneHotEncoder(handle_unknown='ignore')\n", + "\n", + " # Extract features and labels\n", + " X_train = train_data[feature_columns]\n", + " y_train = train_data['label']\n", + "\n", + " numeric_columns = X_train.select_dtypes(include=['float64', 'int64']).columns\n", + " categorical_columns = [col for col in feature_columns if col.startswith('cat')]\n", + "\n", + " X_train.loc[:, numeric_columns] = scaler.fit_transform(X_train[numeric_columns])\n", + " X_train_categorical = encoder.fit_transform(X_train[categorical_columns]).toarray()\n", + "\n", + " # Combine numeric and categorical features\n", + " X_train_processed = pd.concat([\n", + " pd.DataFrame(X_train[numeric_columns], columns=numeric_columns, index=X_train.index),\n", + " pd.DataFrame(X_train_categorical, columns=encoder.get_feature_names_out(categorical_columns),\n", + " index=X_train.index)\n", + " ], axis=1)\n", + "\n", + " # Train the model\n", + " # model = SGDRegressor(**params)\n", + " model = LinearRegression()\n", + " model.fit(X_train_processed, y_train)\n", + "\n", + " # 特征重要性可视化\n", + " if print_feature_importance:\n", + " coefficients = model.coef_\n", + "\n", + " # 创建一个字典,存储特征名称和对应的系数\n", + " feature_importance = dict(zip(X_train_processed.columns, coefficients))\n", + "\n", + " # 按系数绝对值排序\n", + " sorted_importance = sorted(feature_importance.items(), key=lambda x: abs(x[1]), reverse=True)\n", + "\n", + " # 打印特征重要性\n", + " print(\"Feature Importance:\")\n", + " for feature, importance in sorted_importance:\n", + " print(f\"{feature}: {importance:.4f}\")\n", + "\n", + " return model, scaler, encoder" + ], + "outputs": [], + "execution_count": 79 + }, + { + "cell_type": "code", + "id": "beeb098799ecfa6a", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T13:23:56.411933Z", + "start_time": "2025-03-02T13:23:29.076456Z" + } + }, + "source": [ + "print('train data size: ', len(train_data))\n", + "import gc\n", + "\n", + "gc.collect()\n", + "params = {\n", + " 'alpha': 0.0001, # 正则化强度\n", + " 'max_iter': 1000, # 最大迭代次数\n", + " 'tol': 1e-3, # 收敛阈值\n", + " 'eta0': 0.01, # 初始学习率\n", + " 'learning_rate': 'constant'\n", + "}\n", + "\n", + "model, scaler, encoder = train_sgd_model(train_data, feature_columns_new, params)" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train data size: 1137173\n", + "Feature Importance:\n", + "deviation_mean_ema_13: 0.0841\n", + "deviation_mean_ema_20: -0.0679\n", + "ema_13: -0.0630\n", + "ema_20: 0.0550\n", + "industry_ema_20: 0.0238\n", + "industry_ema_13: -0.0227\n", + "act_factor3: -0.0217\n", + "deviation_mean_ema_5: -0.0200\n", + "industry_obv: 0.0178\n", + "industry_obv_deviation: -0.0176\n", + "rsi_6: 0.0155\n", + "act_factor2: 0.0152\n", + "ema_5: 0.0118\n", + "industry_act_factor4: -0.0092\n", + "rsi_3: -0.0091\n", + "deviation_mean_rsi_6: -0.0088\n", + "cat_l2_code_801056.SI: 0.0087\n", + "cat_l2_code_801125.SI: 0.0087\n", + "deviation_mean_act_factor2: -0.0074\n", + "deviation_mean_act_factor3: 0.0073\n", + "industry_act_factor1: 0.0073\n", + "alpha_013: -0.0073\n", + "rank_act_factor2: 0.0072\n", + "cat_l2_code_801039.SI: 0.0070\n", + "cat_l2_code_801194.SI: -0.0069\n", + "cat_l2_code_801782.SI: 0.0067\n", + "industry_act_factor4_deviation: 0.0066\n", + "industry_act_factor1_deviation: -0.0064\n", + "rank_act_factor3: -0.0062\n", + "deviation_mean_alpha_013: 0.0061\n", + "cat_l2_code_801737.SI: 0.0061\n", + "deviation_mean_rank_act_factor2: -0.0058\n", + "industry_act_factor2_deviation: 0.0057\n", + "log_close: -0.0057\n", + "cat_l2_code_801972.SI: -0.0056\n", + "cat_l2_code_801735.SI: 0.0055\n", + "cat_l2_code_801044.SI: 0.0055\n", + "atr_14: -0.0052\n", + "deviation_mean_log_close: 0.0050\n", + "turnover_rate: -0.0050\n", + "cat_l2_code_801995.SI: -0.0049\n", + "cat_l2_code_801081.SI: 0.0049\n", + "399006.SZ_RSI: 0.0048\n", + "cat_l2_code_801191.SI: -0.0048\n", + "deviation_mean_act_factor5: 0.0048\n", + "cat_l2_code_801054.SI: 0.0047\n", + "deviation_mean_atr_14: 0.0047\n", + "deviation_mean_rank_act_factor3: 0.0046\n", + "cat_l2_code_801231.SI: -0.0044\n", + "deviation_mean_rsi_3: 0.0044\n", + "industry_act_factor2: -0.0044\n", + "cat_l2_code_801085.SI: 0.0043\n", + "cat_l2_code_801084.SI: 0.0042\n", + "cat_l2_code_801154.SI: -0.0041\n", + "cat_l2_code_801111.SI: 0.0041\n", + "000905.SH_MACD_hist: 0.0039\n", + "399006.SZ_MACD_hist: -0.0038\n", + "cat_l2_code_801721.SI: -0.0036\n", + "cat_l2_code_801181.SI: -0.0036\n", + "cat_l2_code_801011.SI: 0.0035\n", + "cat_l2_code_801994.SI: -0.0035\n", + "return_10: 0.0034\n", + "cat_l2_code_801076.SI: -0.0034\n", + "cat_l2_code_801017.SI: 0.0033\n", + "cat_l2_code_801016.SI: 0.0033\n", + "deviation_mean_rsi_9: 0.0033\n", + "industry_ema_60: -0.0032\n", + "cat_l2_code_801018.SI: -0.0032\n", + "cat_l2_code_801202.SI: -0.0031\n", + "cat_l2_code_801114.SI: -0.0031\n", + "cat_l2_code_801203.SI: -0.0031\n", + "std_return_5 / std_return_25: 0.0031\n", + "cat_l2_code_801736.SI: 0.0031\n", + "cat_l2_code_801993.SI: -0.0030\n", + "act_factor5: -0.0030\n", + "act_factor6: 0.0030\n", + "industry_act_factor5: -0.0029\n", + "cat_l2_code_801012.SI: -0.0028\n", + "atr_6: 0.0028\n", + "cat_l2_code_801092.SI: -0.0028\n", + "000905.SH_MACD: 0.0028\n", + "cat_l2_code_801077.SI: 0.0028\n", + "cat_l2_code_801112.SI: -0.0028\n", + "cat_l2_code_801744.SI: -0.0028\n", + "cat_l2_code_801055.SI: 0.0027\n", + "cat_l2_code_801034.SI: 0.0027\n", + "deviation_mean_atr_6: -0.0027\n", + "cat_l2_code_801183.SI: -0.0026\n", + "cat_l2_code_801769.SI: -0.0026\n", + "deviation_mean_act_factor6: -0.0025\n", + "industry_return_20: 0.0024\n", + "deviation_mean_turnover_rate: 0.0024\n", + "cat_l2_code_801952.SI: 0.0024\n", + "industry_return_20_deviation: -0.0024\n", + "000905.SH_RSI: -0.0024\n", + "cat_l2_code_801971.SI: -0.0023\n", + "deviation_mean_std_return_5 / std_return_25: -0.0023\n", + "cat_l2_code_801095.SI: 0.0023\n", + "act_factor4: 0.0023\n", + "cat_l2_code_801178.SI: -0.0023\n", + "std_return_15: 0.0023\n", + "rsi_9: -0.0023\n", + "deviation_mean_return_10: -0.0022\n", + "cat_l2_code_801712.SI: 0.0022\n", + "cat_l2_code_801731.SI: 0.0022\n", + "up: 0.0022\n", + "cat_l2_code_801083.SI: 0.0021\n", + "cat_l2_code_801204.SI: -0.0021\n", + "cat_l2_code_801723.SI: -0.0021\n", + "cat_l2_code_801992.SI: -0.0021\n", + "cat_l2_code_801155.SI: -0.0021\n", + "cat_l2_code_801179.SI: -0.0021\n", + "alpha_007: -0.0020\n", + "std_return_25: 0.0020\n", + "cat_l2_code_801742.SI: -0.0020\n", + "deviation_mean_avg_close_5: -0.0020\n", + "deviation_mean_std_return_25: -0.0019\n", + "cat_l2_code_801767.SI: -0.0019\n", + "cat_l2_code_801086.SI: 0.0019\n", + "deviation_mean_up: -0.0019\n", + "act_factor1: -0.0019\n", + "industry_act_factor3_deviation: 0.0019\n", + "std_return_5: 0.0018\n", + "deviation_mean_std_return_90 - std_return_90_2: 0.0018\n", + "000852.SH_Signal_line: 0.0018\n", + "deviation_mean_std_return_90_2: 0.0018\n", + "cat_l2_code_801218.SI: -0.0018\n", + "rank_act_factor1: -0.0017\n", + "deviation_mean_std_return_15: -0.0017\n", + "000905.SH_Signal_line: 0.0017\n", + "cat_l2_code_801783.SI: -0.0017\n", + "deviation_mean_act_factor4: -0.0016\n", + "std_return_90 - std_return_90_2: -0.0016\n", + "industry_return_5_deviation: -0.0016\n", + "cat_l2_code_801045.SI: 0.0016\n", + "cat_l2_code_801765.SI: -0.0016\n", + "industry_rank_act_factor3: -0.0016\n", + "cat_l2_code_801784.SI: -0.0015\n", + "industry_rank_act_factor2: 0.0015\n", + "cat_l2_code_801082.SI: 0.0015\n", + "cat_l2_code_801014.SI: 0.0015\n", + "cat_l2_code_801141.SI: -0.0015\n", + "cat_l2_code_801124.SI: 0.0015\n", + "cat_af2_1.0: -0.0015\n", + "cat_af2_0.0: 0.0015\n", + "cat_l2_code_801113.SI: 0.0014\n", + "cat_l2_code_801142.SI: -0.0014\n", + "cat_l2_code_801015.SI: -0.0014\n", + "cat_l2_code_801104.SI: 0.0014\n", + "399006.SZ_daily_return: 0.0014\n", + "000852.SH_MACD: 0.0013\n", + "alpha_003: -0.0013\n", + "cat_l2_code_801219.SI: -0.0013\n", + "deviation_mean_ema_60: 0.0013\n", + "industry_ema_5: 0.0013\n", + "cat_l2_code_801078.SI: 0.0013\n", + "000852.SH_MACD_hist: -0.0012\n", + "industry_rank_act_factor1: -0.0012\n", + "cat_l2_code_801101.SI: 0.0012\n", + "cat_l2_code_801033.SI: 0.0012\n", + "cat_l2_code_801152.SI: -0.0012\n", + "cat_l2_code_801128.SI: -0.0012\n", + "return_5: -0.0012\n", + "deviation_mean_std_return_90: -0.0012\n", + "down: -0.0012\n", + "deviation_mean_rank_act_factor1: 0.0012\n", + "cat_l2_code_801096.SI: -0.0011\n", + "deviation_mean_log(circ_mv): -0.0011\n", + "deviation_mean_alpha_007: 0.0011\n", + "maobv_6: -0.0011\n", + "ema_60: -0.0011\n", + "cat_l2_code_801963.SI: -0.0011\n", + "cat_l2_code_801738.SI: -0.0011\n", + "cat_l2_code_801093.SI: 0.0011\n", + "obv: -0.0011\n", + "cat_l2_code_801074.SI: 0.0011\n", + "cat_l2_code_801193.SI: 0.0010\n", + "log(circ_mv): 0.0010\n", + "cat_l2_code_801785.SI: -0.0010\n", + "cat_l2_code_801115.SI: -0.0010\n", + "cat_l2_code_801206.SI: 0.0010\n", + "cat_l2_code_801724.SI: -0.0010\n", + "deviation_mean_maobv_6: 0.0010\n", + "cat_l2_code_801991.SI: -0.0009\n", + "cat_l2_code_801161.SI: -0.0009\n", + "deviation_mean_obv: 0.0009\n", + "cat_l2_code_801143.SI: 0.0009\n", + "cat_l2_code_801053.SI: 0.0008\n", + "cat_l2_code_801156.SI: -0.0008\n", + "volume_ratio: -0.0008\n", + "cat_l2_code_801962.SI: -0.0008\n", + "std_return_90: -0.0008\n", + "cat_l2_code_801711.SI: 0.0008\n", + "buy_elg_vol_minus_sell_elg_vol: -0.0008\n", + "cat_l2_code_801038.SI: -0.0008\n", + "cat_l2_code_801072.SI: 0.0008\n", + "cat_l2_code_801037.SI: 0.0007\n", + "deviation_mean_active_buy_volume_large: -0.0007\n", + "cat_l2_code_801981.SI: -0.0007\n", + "active_buy_volume_large: 0.0007\n", + "cat_l2_code_801032.SI: -0.0007\n", + "deviation_mean_buy_elg_vol_minus_sell_elg_vol: 0.0007\n", + "399006.SZ_MACD: -0.0007\n", + "obv-maobv_6: 0.0007\n", + "cat_l2_code_801733.SI: 0.0006\n", + "cat_l2_code_801103.SI: -0.0006\n", + "deviation_mean_obv-maobv_6: -0.0006\n", + "std_return_5 / std_return_90: -0.0006\n", + "cat_l2_code_801163.SI: -0.0006\n", + "000852.SH_daily_return: -0.0006\n", + "cat_l2_code_801043.SI: 0.0006\n", + "cat_l2_code_801131.SI: -0.0005\n", + "cat_af1_1.0: -0.0005\n", + "cat_af1_0.0: 0.0005\n", + "cat_l2_code_801764.SI: -0.0005\n", + "cat_l2_code_801153.SI: -0.0005\n", + "deviation_mean_act_factor1: -0.0005\n", + "cat_l2_code_801951.SI: -0.0005\n", + "cat_l2_code_801726.SI: -0.0005\n", + "deviation_mean_std_return_5: -0.0005\n", + "cat_l2_code_801036.SI: 0.0005\n", + "industry_act_factor6: 0.0005\n", + "industry_return_5_percentile: 0.0005\n", + "cat_af4_1.0: 0.0005\n", + "cat_af4_0.0: -0.0005\n", + "cat_l2_code_801223.SI: -0.0005\n", + "cat_l2_code_801881.SI: -0.0004\n", + "399006.SZ_Signal_line: 0.0004\n", + "cat_l2_code_801743.SI: -0.0004\n", + "deviation_mean_active_buy_volume_small: 0.0004\n", + "return_20: -0.0004\n", + "000905.SH_daily_return: -0.0003\n", + "cat_l2_code_801982.SI: -0.0003\n", + "pe_ttm: -0.0003\n", + "cat_l2_code_801713.SI: 0.0003\n", + "cat_l2_code_801129.SI: 0.0003\n", + "active_buy_volume_small: -0.0003\n", + "deviation_mean_return_5: -0.0003\n", + "deviation_mean_399006.SZ_Signal_line: -0.0003\n", + "cat_l2_code_801126.SI: 0.0003\n", + "deviation_mean_pe_ttm: 0.0002\n", + "deviation_mean_alpha_003: 0.0002\n", + "cat_l2_code_801116.SI: -0.0002\n", + "cat_af3_0.0: 0.0002\n", + "cat_af3_1.0: -0.0002\n", + "cat_l2_code_801132.SI: -0.0002\n", + "deviation_mean_399006.SZ_daily_return: 0.0002\n", + "std_return_90_2: -0.0002\n", + "deviation_mean_alpha_022: 0.0002\n", + "deviation_mean_std_return_5 / std_return_90: -0.0002\n", + "deviation_mean_000852.SH_RSI: -0.0002\n", + "deviation_mean_return_20: 0.0002\n", + "deviation_mean_down: -0.0002\n", + "active_buy_volume_big: 0.0002\n", + "deviation_mean_volume_ratio: -0.0002\n", + "cat_l2_code_801133.SI: 0.0002\n", + "industry_act_factor3: 0.0002\n", + "alpha_022: -0.0001\n", + "deviation_mean_399006.SZ_MACD: -0.0001\n", + "avg_close_5: -0.0001\n", + "deviation_mean_000852.SH_MACD: 0.0001\n", + "cat_l2_code_801722.SI: -0.0001\n", + "cat_l2_code_801766.SI: 0.0001\n", + "deviation_mean_active_buy_volume_big: -0.0001\n", + "cat_l2_code_801127.SI: -0.0001\n", + "deviation_mean_399006.SZ_MACD_hist: 0.0001\n", + "deviation_mean_000905.SH_Signal_line: -0.0001\n", + "cat_l2_code_801745.SI: -0.0001\n", + "deviation_mean_000905.SH_RSI: 0.0001\n", + "deviation_mean_000905.SH_MACD_hist: -0.0001\n", + "deviation_mean_399006.SZ_RSI: -0.0001\n", + "industry_return_5: 0.0001\n", + "cat_l2_code_801102.SI: -0.0001\n", + "cat_l2_code_801741.SI: 0.0001\n", + "000852.SH_RSI: 0.0001\n", + "deviation_mean_000852.SH_daily_return: -0.0001\n", + "deviation_mean_000852.SH_Signal_line: -0.0001\n", + "deviation_mean_000852.SH_MACD_hist: -0.0001\n", + "cat_l2_code_801145.SI: -0.0000\n", + "deviation_mean_buy_lg_vol_minus_sell_lg_vol: 0.0000\n", + "cat_l2_code_801051.SI: -0.0000\n", + "cat_l2_code_801151.SI: -0.0000\n", + "deviation_mean_000905.SH_daily_return: -0.0000\n", + "deviation_mean_000905.SH_MACD: -0.0000\n", + "buy_lg_vol_minus_sell_lg_vol: 0.0000\n" + ] + } + ], + "execution_count": 80 + }, + { + "cell_type": "code", + "id": "465944b1d463e4b1", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T13:23:57.174623Z", + "start_time": "2025-03-02T13:23:57.148826Z" + } + }, + "source": [ + "from tqdm import tqdm\n", + "\n", + "\n", + "def incremental_training(test_data: pd.DataFrame,\n", + " model,\n", + " scaler,\n", + " encoder,\n", + " days: int,\n", + " back_days: int,\n", + " feature_columns: list,\n", + " params: dict\n", + " ):\n", + " test_data = test_data.sort_values(by='trade_date')\n", + " scores = []\n", + " unique_trade_dates = sorted(test_data['trade_date'].unique())\n", + "\n", + " new_model = None\n", + " for i in tqdm(range(0, len(unique_trade_dates))):\n", + " # Get the current window of trade dates\n", + " current_dates = [unique_trade_dates[i]]\n", + " window_data = test_data[test_data['trade_date'].isin(current_dates)]\n", + " X = window_data[feature_columns]\n", + " numeric_columns = X.select_dtypes(include=['float64', 'int64']).columns\n", + " categorical_columns = [col for col in feature_columns if col.startswith('cat')]\n", + " X.loc[:, numeric_columns] = scaler.transform(X[numeric_columns])\n", + " X_categorical = encoder.transform(X[categorical_columns]).toarray()\n", + "\n", + " # Combine numeric and categorical features\n", + " X_processed = pd.concat([\n", + " pd.DataFrame(X[numeric_columns], columns=numeric_columns, index=X.index),\n", + " pd.DataFrame(X_categorical, columns=encoder.get_feature_names_out(categorical_columns), index=X.index)\n", + " ], axis=1)\n", + " X_processed = X_processed.fillna(0)\n", + "\n", + " if new_model is not None:\n", + " window_scores = new_model.predict(X_processed)\n", + " else:\n", + " window_scores = model.predict(X_processed)\n", + " scores.extend(window_scores)\n", + "\n", + " # # Prepare data for incremental training\n", + " # current_dates = unique_trade_dates[max(0, i - back_days):i + days]\n", + " # window_data = test_data[test_data['trade_date'].isin(current_dates)]\n", + " # X_train = window_data[feature_columns]\n", + " current_dates = unique_trade_dates[max(0, i - days - back_days):i + 1 - back_days]\n", + " window_data = test_data[test_data['trade_date'].isin(current_dates)]\n", + " window_data['label'] = remove_outliers_label_percentile(window_data['label'], log=False)\n", + " window_data = window_data.dropna(subset=feature_columns)\n", + " window_data = window_data.dropna(subset=['label'])\n", + " X_train = window_data[feature_columns]\n", + " y_train = window_data['label']\n", + " # Incrementally train the model\n", + " if len(y_train.unique()) > 1:\n", + " X_train.loc[:, numeric_columns] = scaler.transform(X_train[numeric_columns])\n", + " X_train_categorical = encoder.transform(X_train[categorical_columns]).toarray()\n", + " X_train_processed = pd.concat([\n", + " pd.DataFrame(X_train[numeric_columns], columns=numeric_columns, index=X_train.index),\n", + " pd.DataFrame(X_train_categorical, columns=encoder.get_feature_names_out(categorical_columns),\n", + " index=X_train.index)\n", + " ], axis=1)\n", + " X_train_processed = X_train_processed.fillna(0)\n", + " model = model.partial_fit(X_train_processed, y_train)\n", + " else:\n", + " print(current_dates)\n", + "\n", + " # Add the scores as a new 'score' column to the test_data\n", + " test_data['score'] = scores\n", + " return test_data" + ], + "outputs": [], + "execution_count": 81 + }, + { + "cell_type": "code", + "id": "e3ac761d8f0b5d31", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T13:25:27.698736Z", + "start_time": "2025-03-02T13:25:25.801431Z" + } + }, + "source": [ + "import gc\n", + "\n", + "gc.collect()\n", + "\n", + "# predictions_test = incremental_training(test_data, model, scaler, encoder, 10, days, feature_columns_new, params)\n", + "X_test = test_data[feature_columns_new]\n", + "numeric_columns = X_test.select_dtypes(include=['float64', 'int64']).columns\n", + "categorical_columns = [col for col in feature_columns if col.startswith('cat')]\n", + "\n", + "X_test.loc[:, numeric_columns] = scaler.transform(X_test[numeric_columns])\n", + "X_test_categorical = encoder.transform(X_test[categorical_columns]).toarray()\n", + "\n", + "# Combine numeric and categorical features\n", + "X_test_processed = pd.concat([\n", + " pd.DataFrame(X_test[numeric_columns], columns=numeric_columns, index=X_test.index),\n", + " pd.DataFrame(X_test_categorical, columns=encoder.get_feature_names_out(categorical_columns), index=X_test.index)\n", + "], axis=1)\n", + "predictions_test = test_data[['ts_code', 'trade_date']]\n", + "predictions_test['score'] = model.predict(X_test_processed)\n", + "predictions_test = predictions_test.loc[predictions_test.groupby('trade_date')['score'].idxmax()]\n", + "predictions_test[['trade_date', 'score', 'ts_code']].to_csv('predictions_test.tsv', index=False)\n" + ], + "outputs": [], + "execution_count": 84 + }, + { + "cell_type": "code", + "id": "b427ce41-9739-4e9e-bea8-5f2551fec5d7", + "metadata": { + "jupyter": { + "source_hidden": true + }, + "ExecuteTime": { + "end_time": "2025-03-02T13:24:01.404626100Z", + "start_time": "2025-03-02T13:17:17.216317Z" + } + }, + "source": [ + "import joblib\n", + "import lightgbm as lgb\n", + "import pandas as pd\n", + "\n", + "\n", + "# 假设你已经训练好了一个 LightGBM 模型\n", + "# model = lgb.train(params, train_data, ...)\n", + "\n", + "def save_model_with_info(model, params, feature_columns, train_data, info, save_path):\n", + " \"\"\"\n", + " 保存 LightGBM 模型及其相关信息。\n", + " \n", + " 参数:\n", + " model: 训练好的 LightGBM 模型 (lgb.Booster)。\n", + " params: 模型的参数 (dict)。\n", + " feature_columns: 特征列名列表 (list)。\n", + " train_data: 训练数据 (pd.DataFrame),包含 'trade_date' 列。\n", + " info: 额外信息 (str 或 dict)。\n", + " save_path: 保存路径 (str)。\n", + " \"\"\"\n", + " # 提取训练数据的 trade_date 的最大值和最小值\n", + " if 'trade_date' not in train_data.columns:\n", + " raise ValueError(\"训练数据中必须包含 'trade_date' 列。\")\n", + "\n", + " trade_date_min = train_data['trade_date'].min()\n", + " trade_date_max = train_data['trade_date'].max()\n", + "\n", + " # 构建保存的信息字典\n", + " model_info = {\n", + " 'model': model, # 保存模型本身\n", + " 'params': params, # 模型参数\n", + " 'feature_columns': feature_columns, # 特征列名\n", + " 'trade_date_range': {\n", + " 'min': trade_date_min,\n", + " 'max': trade_date_max\n", + " }, # trade_date 的范围\n", + " 'info': info # 额外信息\n", + " }\n", + "\n", + " # 使用 joblib 保存模型及相关信息\n", + " joblib.dump(model_info, save_path)\n", + " print(f\"模型及相关信息已成功保存到 {save_path}\")\n", + "\n", + "# info = \"Update Regression + 滚动new model + 5days\"\n", + "\n", + "# # 保存模型及相关信息\n", + "# save_path = \"../model/lightgbm_model_UpdateRegression_2025-2-25.pkl\"\n", + "# save_model_with_info(model, light_params, feature_columns, train_data, info, save_path)" + ], + "outputs": [], + "execution_count": 73 + }, + { + "cell_type": "code", + "id": "8f9a2b7b-11fe-4eb5-aa11-c4066fe418a1", + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-02T13:24:01.407058800Z", + "start_time": "2025-03-02T13:17:17.289631Z" + } + }, + "source": [], + "outputs": [], + "execution_count": null + } + ], + "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.8.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/code/train/V1.2.ipynb b/code/train/V1.2.ipynb deleted file mode 100644 index cd10a33..0000000 --- a/code/train/V1.2.ipynb +++ /dev/null @@ -1,1104 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "id": "79a7758178bafdd3", - "metadata": { - "jupyter": { - "source_hidden": true - }, - "ExecuteTime": { - "end_time": "2025-02-14T17:33:40.203900Z", - "start_time": "2025-02-14T17:33:40.121936Z" - } - }, - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - "\n", - "import pandas as pd\n", - "def read_and_merge_h5_data(h5_filename, key, columns, df=None, join='left'):\n", - " \"\"\"\n", - " 读取 HDF5 文件中的数据,根据指定的 columns 筛选数据,\n", - " 如果传入 df 参数,则将其与读取的数据根据 ts_code 和 trade_date 合并。\n", - "\n", - " 参数:\n", - " - h5_filename: HDF5 文件名\n", - " - key: 数据存储在 HDF5 文件中的 key\n", - " - columns: 要读取的列名列表\n", - " - df: 需要合并的 DataFrame(如果为空,则不进行合并)\n", - "\n", - " 返回:\n", - " - 合并后的 DataFrame\n", - " \"\"\"\n", - " # 处理 _ 开头的列名\n", - " processed_columns = []\n", - " for col in columns:\n", - " if col.startswith('_'):\n", - " processed_columns.append(col[1:]) # 去掉下划线\n", - " else:\n", - " processed_columns.append(col)\n", - "\n", - " # 从 HDF5 文件读取数据,选择需要的列\n", - " data = pd.read_hdf(h5_filename, key=key, columns=processed_columns)\n", - "\n", - " # 修改列名,如果列名以前有 _,加上 _\n", - " for col in data.columns:\n", - " if col not in columns: # 只有不在 columns 中的列才需要加下划线\n", - " new_col = f'_{col}'\n", - " data.rename(columns={col: new_col}, inplace=True)\n", - "\n", - " # 如果传入的 df 不为空,则进行合并\n", - " if df is not None and not df.empty:\n", - " # 确保两个 DataFrame 都有 ts_code 和 trade_date 列\n", - " df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')\n", - " data['trade_date'] = pd.to_datetime(data['trade_date'], format='%Y%m%d')\n", - "\n", - " # 根据 ts_code 和 trade_date 合并\n", - " merged_df = pd.merge(df, data, on=['ts_code', 'trade_date'], how=join)\n", - " else:\n", - " # 如果 df 为空,则直接返回读取的数据\n", - " merged_df = data\n", - "\n", - " return merged_df\n", - "\n" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The autoreload extension is already loaded. To reload it, use:\n", - " %reload_ext autoreload\n" - ] - } - ], - "execution_count": 32 - }, - { - "cell_type": "code", - "id": "a79cafb06a7e0e43", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T17:34:23.845554Z", - "start_time": "2025-02-14T17:33:40.211865Z" - } - }, - "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'],\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)" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "daily data\n", - "daily basic\n", - "stk limit\n", - "money flow\n" - ] - } - ], - "execution_count": 33 - }, - { - "cell_type": "code", - "id": "c4e9e1d31da6dba6", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T17:34:23.956070Z", - "start_time": "2025-02-14T17:34:23.878555Z" - } - }, - "source": [ - "origin_columns = df.columns.tolist()\n", - "origin_columns = [col for col in origin_columns if col not in ['turnover_rate', 'pe_ttm', 'volume_ratio']]" - ], - "outputs": [], - "execution_count": 34 - }, - { - "cell_type": "code", - "id": "a735bc02ceb4d872", - "metadata": { - "jupyter": { - "source_hidden": true - }, - "ExecuteTime": { - "end_time": "2025-02-14T17:34:24.082032Z", - "start_time": "2025-02-14T17:34:23.990152Z" - } - }, - "source": [ - "import numpy as np\n", - "import talib\n", - "\n", - "def get_technical_factor(df):\n", - " # 按股票和日期排序\n", - " df = df.sort_values(by=['ts_code', 'trade_date'])\n", - " grouped = df.groupby('ts_code', group_keys=False)\n", - "\n", - " # 计算 up 和 down\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", - " # 计算 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), 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), 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", - " df['obv-maobv_6'] = df['obv'] - df['maobv_6']\n", - "\n", - " # 计算 RSI\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_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", - " # 计算 avg_close_5\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", - " # 计算比值指标\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", - " return df\n", - "\n", - "\n", - "def get_act_factor(df):\n", - " # 按股票和日期排序\n", - " df = df.sort_values(by=['ts_code', 'trade_date'])\n", - " grouped = df.groupby('ts_code', group_keys=False)\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", - " # 计算 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(df['act_factor1']**2 + df['act_factor2']**2)\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", - " return df\n", - "\n", - "\n", - "def get_money_flow_factor(df):\n", - " # 计算资金流相关因子(字段名称见 tushare 数据说明)\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", - " return df\n", - "\n", - "\n", - "def get_alpha_factor(df):\n", - " df = df.sort_values(by=['ts_code', 'trade_date'])\n", - " grouped = df.groupby('ts_code')\n", - "\n", - " # alpha_022: 当前 close 与 5 日前 close 差值\n", - " df['alpha_022'] = grouped['close'].transform(lambda x: x - x.shift(5))\n", - "\n", - " # alpha_003: (close - open) / (high - low)\n", - " df['alpha_003'] = np.where(df['high'] != df['low'],\n", - " (df['close'] - df['open']) / (df['high'] - df['low']),\n", - " 0)\n", - "\n", - " # alpha_007: 计算过去5日 close 与 vol 的相关性,并按 trade_date 排名\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", - " # alpha_013: 计算过去5日 close 之和 - 20日 close 之和,并按 trade_date 排名\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", - " return df\n", - "\n", - "\n", - "def get_future_data(df):\n", - " df = df.sort_values(by=['ts_code', 'trade_date'])\n", - " # 预先对 ts_code 分组,使用 transform 保持原 DataFrame 形状\n", - " grouped = df.groupby('ts_code')\n", - "\n", - " df['future_return1'] = (grouped['close'].transform(lambda x: x.shift(-1)) - df['close']) / df['close']\n", - " df['future_return2'] = (grouped['open'].transform(lambda x: x.shift(-2)) - grouped['open'].transform(lambda x: x.shift(-1))) / grouped['open'].transform(lambda x: x.shift(-1))\n", - " df['future_return3'] = (grouped['close'].transform(lambda x: x.shift(-2)) - grouped['close'].transform(lambda x: x.shift(-1))) / grouped['close'].transform(lambda x: x.shift(-1))\n", - " df['future_return4'] = (grouped['close'].transform(lambda x: x.shift(-2)) - grouped['open'].transform(lambda x: x.shift(-1))) / grouped['open'].transform(lambda x: x.shift(-1))\n", - " df['future_return5'] = (grouped['close'].transform(lambda x: x.shift(-5)) - grouped['open'].transform(lambda x: x.shift(-1))) / grouped['open'].transform(lambda x: x.shift(-1))\n", - " df['future_return6'] = (grouped['close'].transform(lambda x: x.shift(-10)) - grouped['open'].transform(lambda x: x.shift(-1))) / grouped['open'].transform(lambda x: x.shift(-1))\n", - " df['future_return7'] = (grouped['close'].transform(lambda x: x.shift(-20)) - grouped['open'].transform(lambda x: x.shift(-1))) / grouped['open'].transform(lambda x: x.shift(-1))\n", - "\n", - " df['future_close1'] = (grouped['close'].transform(lambda x: x.shift(-1)) - df['close']) / df['close']\n", - " df['future_close2'] = (grouped['close'].transform(lambda x: x.shift(-2)) - df['close']) / df['close']\n", - " df['future_close3'] = (grouped['close'].transform(lambda x: x.shift(-3)) - df['close']) / df['close']\n", - " df['future_close4'] = (grouped['close'].transform(lambda x: x.shift(-4)) - df['close']) / df['close']\n", - " df['future_close5'] = (grouped['close'].transform(lambda x: x.shift(-5)) - df['close']) / df['close']\n", - "\n", - " df['future_af11'] = grouped['act_factor1'].transform(lambda x: x.shift(-1))\n", - " df['future_af12'] = grouped['act_factor1'].transform(lambda x: x.shift(-2))\n", - " df['future_af13'] = grouped['act_factor1'].transform(lambda x: x.shift(-3))\n", - " df['future_af14'] = grouped['act_factor1'].transform(lambda x: x.shift(-4))\n", - " df['future_af15'] = grouped['act_factor1'].transform(lambda x: x.shift(-5))\n", - "\n", - " df['future_af21'] = grouped['act_factor2'].transform(lambda x: x.shift(-1))\n", - " df['future_af22'] = grouped['act_factor2'].transform(lambda x: x.shift(-2))\n", - " df['future_af23'] = grouped['act_factor2'].transform(lambda x: x.shift(-3))\n", - " df['future_af24'] = grouped['act_factor2'].transform(lambda x: x.shift(-4))\n", - " df['future_af25'] = grouped['act_factor2'].transform(lambda x: x.shift(-5))\n", - "\n", - " df['future_af31'] = grouped['act_factor3'].transform(lambda x: x.shift(-1))\n", - " df['future_af32'] = grouped['act_factor3'].transform(lambda x: x.shift(-2))\n", - " df['future_af33'] = grouped['act_factor3'].transform(lambda x: x.shift(-3))\n", - " df['future_af34'] = grouped['act_factor3'].transform(lambda x: x.shift(-4))\n", - " df['future_af35'] = grouped['act_factor3'].transform(lambda x: x.shift(-5))\n", - "\n", - " return df\n" - ], - "outputs": [], - "execution_count": 35 - }, - { - "cell_type": "code", - "id": "53f86ddc0677a6d7", - "metadata": { - "scrolled": true, - "ExecuteTime": { - "end_time": "2025-02-14T17:36:10.108321Z", - "start_time": "2025-02-14T17:34:24.118116Z" - } - }, - "source": [ - "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_future_data(df)\n", - "# df = df.drop(columns=origin_columns)\n", - "\n", - "print(df.info())" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Index: 8296325 entries, 1962 to 8296323\n", - "Data columns (total 65 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 turnover_rate float64 \n", - " 8 pe_ttm float64 \n", - " 9 circ_mv float64 \n", - " 10 volume_ratio float64 \n", - " 11 is_st bool \n", - " 12 up_limit float64 \n", - " 13 down_limit float64 \n", - " 14 buy_sm_vol float64 \n", - " 15 sell_sm_vol float64 \n", - " 16 buy_lg_vol float64 \n", - " 17 sell_lg_vol float64 \n", - " 18 buy_elg_vol float64 \n", - " 19 sell_elg_vol float64 \n", - " 20 net_mf_vol float64 \n", - " 21 up float64 \n", - " 22 down float64 \n", - " 23 atr_14 float64 \n", - " 24 atr_6 float64 \n", - " 25 obv float64 \n", - " 26 maobv_6 float64 \n", - " 27 obv-maobv_6 float64 \n", - " 28 rsi_3 float64 \n", - " 29 rsi_6 float64 \n", - " 30 rsi_9 float64 \n", - " 31 return_10 float64 \n", - " 32 return_20 float64 \n", - " 33 avg_close_5 float64 \n", - " 34 std_return_5 float64 \n", - " 35 std_return_15 float64 \n", - " 36 std_return_25 float64 \n", - " 37 std_return_90 float64 \n", - " 38 std_return_90_2 float64 \n", - " 39 std_return_5 / std_return_90 float64 \n", - " 40 std_return_5 / std_return_25 float64 \n", - " 41 std_return_90 - std_return_90_2 float64 \n", - " 42 ema_5 float64 \n", - " 43 ema_13 float64 \n", - " 44 ema_20 float64 \n", - " 45 ema_60 float64 \n", - " 46 act_factor1 float64 \n", - " 47 act_factor2 float64 \n", - " 48 act_factor3 float64 \n", - " 49 act_factor4 float64 \n", - " 50 act_factor5 float64 \n", - " 51 act_factor6 float64 \n", - " 52 rank_act_factor1 float64 \n", - " 53 rank_act_factor2 float64 \n", - " 54 rank_act_factor3 float64 \n", - " 55 active_buy_volume_large float64 \n", - " 56 active_buy_volume_big float64 \n", - " 57 active_buy_volume_small float64 \n", - " 58 buy_lg_vol_minus_sell_lg_vol float64 \n", - " 59 buy_elg_vol_minus_sell_elg_vol float64 \n", - " 60 log(circ_mv) float64 \n", - " 61 alpha_022 float64 \n", - " 62 alpha_003 float64 \n", - " 63 alpha_007 float64 \n", - " 64 alpha_013 float64 \n", - "dtypes: bool(1), datetime64[ns](1), float64(62), object(1)\n", - "memory usage: 4.0+ GB\n", - "None\n" - ] - } - ], - "execution_count": 36 - }, - { - "cell_type": "code", - "id": "dbe2fd8021b9417f", - "metadata": { - "jupyter": { - "source_hidden": true - }, - "scrolled": true, - "ExecuteTime": { - "end_time": "2025-02-14T17:36:52.088380Z", - "start_time": "2025-02-14T17:36:10.512236Z" - } - }, - "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.reset_index(drop=True)\n", - " return df\n", - "\n", - "\n", - "df = filter_data(df)\n", - "print(df.info())" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "RangeIndex: 5453316 entries, 0 to 5453315\n", - "Data columns (total 65 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 turnover_rate float64 \n", - " 8 pe_ttm float64 \n", - " 9 circ_mv float64 \n", - " 10 volume_ratio float64 \n", - " 11 is_st bool \n", - " 12 up_limit float64 \n", - " 13 down_limit float64 \n", - " 14 buy_sm_vol float64 \n", - " 15 sell_sm_vol float64 \n", - " 16 buy_lg_vol float64 \n", - " 17 sell_lg_vol float64 \n", - " 18 buy_elg_vol float64 \n", - " 19 sell_elg_vol float64 \n", - " 20 net_mf_vol float64 \n", - " 21 up float64 \n", - " 22 down float64 \n", - " 23 atr_14 float64 \n", - " 24 atr_6 float64 \n", - " 25 obv float64 \n", - " 26 maobv_6 float64 \n", - " 27 obv-maobv_6 float64 \n", - " 28 rsi_3 float64 \n", - " 29 rsi_6 float64 \n", - " 30 rsi_9 float64 \n", - " 31 return_10 float64 \n", - " 32 return_20 float64 \n", - " 33 avg_close_5 float64 \n", - " 34 std_return_5 float64 \n", - " 35 std_return_15 float64 \n", - " 36 std_return_25 float64 \n", - " 37 std_return_90 float64 \n", - " 38 std_return_90_2 float64 \n", - " 39 std_return_5 / std_return_90 float64 \n", - " 40 std_return_5 / std_return_25 float64 \n", - " 41 std_return_90 - std_return_90_2 float64 \n", - " 42 ema_5 float64 \n", - " 43 ema_13 float64 \n", - " 44 ema_20 float64 \n", - " 45 ema_60 float64 \n", - " 46 act_factor1 float64 \n", - " 47 act_factor2 float64 \n", - " 48 act_factor3 float64 \n", - " 49 act_factor4 float64 \n", - " 50 act_factor5 float64 \n", - " 51 act_factor6 float64 \n", - " 52 rank_act_factor1 float64 \n", - " 53 rank_act_factor2 float64 \n", - " 54 rank_act_factor3 float64 \n", - " 55 active_buy_volume_large float64 \n", - " 56 active_buy_volume_big float64 \n", - " 57 active_buy_volume_small float64 \n", - " 58 buy_lg_vol_minus_sell_lg_vol float64 \n", - " 59 buy_elg_vol_minus_sell_elg_vol float64 \n", - " 60 log(circ_mv) float64 \n", - " 61 alpha_022 float64 \n", - " 62 alpha_003 float64 \n", - " 63 alpha_007 float64 \n", - " 64 alpha_013 float64 \n", - "dtypes: bool(1), datetime64[ns](1), float64(62), object(1)\n", - "memory usage: 2.6+ GB\n", - "None\n" - ] - } - ], - "execution_count": 37 - }, - { - "cell_type": "code", - "id": "5f3d9aece75318cd", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T17:37:16.249760Z", - "start_time": "2025-02-14T17:36:52.387540Z" - } - }, - "source": [ - "train_data = df[df['trade_date'] <= '2023-01-01']\n", - "test_data = df[df['trade_date'] >= '2023-01-01']\n", - "\n", - "train_data = train_data.groupby('trade_date', group_keys=False).apply(lambda x: x.nlargest(1000, 'return_20'))\n", - "test_data = test_data.groupby('trade_date', group_keys=False).apply(lambda x: x.nlargest(1000, 'return_20'))\n", - "\n", - "train_data = get_future_data(train_data)\n", - "\n", - "feature_columns = [col for col in df.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 'score' not in col]\n", - "feature_columns = [col for col in feature_columns if col not in origin_columns]\n", - "\n", - "# for column in [column for column in train_data.columns if 'future' in column]:\n", - "# label_index = neutralize_labels(train_data[column], train_data, feature_columns, z_threshold=3, method='regression')\n", - "# train_data = train_data[label_index]\n", - "# label_index = neutralize_labels(test_data[column], test_data, feature_columns, z_threshold=3, method='regression')\n", - "# test_data = test_data[label_index]\n", - "\n", - "df = df[['ts_code', 'trade_date', 'open', 'close']]\n", - "print(feature_columns)\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')}\")" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['turnover_rate', 'pe_ttm', 'volume_ratio', 'up', 'down', 'atr_14', 'atr_6', 'obv', 'maobv_6', 'obv-maobv_6', 'rsi_3', 'rsi_6', 'rsi_9', 'return_10', 'return_20', 'avg_close_5', 'std_return_5', 'std_return_15', 'std_return_25', 'std_return_90', 'std_return_90_2', 'std_return_5 / std_return_90', 'std_return_5 / std_return_25', 'std_return_90 - std_return_90_2', 'ema_5', 'ema_13', 'ema_20', 'ema_60', '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', 'buy_lg_vol_minus_sell_lg_vol', 'buy_elg_vol_minus_sell_elg_vol', 'log(circ_mv)', 'alpha_022', 'alpha_003', 'alpha_007', 'alpha_013']\n", - "最小日期: 2017-01-03\n", - "最大日期: 2022-12-30\n", - "507000\n", - "最小日期: 2023-01-03\n", - "最大日期: 2025-02-12\n" - ] - } - ], - "execution_count": 38 - }, - { - "cell_type": "code", - "id": "f4f16d63ad18d1bc", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T19:01:09.964760Z", - "start_time": "2025-02-14T19:01:05.892897Z" - } - }, - "source": [ - "def get_qcuts(series, quantiles):\n", - " q = pd.qcut(series, q=quantiles, labels=False, duplicates='drop')\n", - " return q[-1] # 返回窗口最后一个元素的分位数标签\n", - "\n", - "\n", - "window = 5\n", - "quantiles = 20\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['past_close'] = df.groupby('ts_code')['close'].shift(days)\n", - " df['future_return'] = (df['future_close'] - df['past_close']) / df['past_close']\n", - "\n", - " df['future_volatility'] = df.groupby('ts_code')['future_return'].rolling(days, min_periods=1).std().reset_index(level=0, drop=True)\n", - "\n", - " df['sharpe_ratio'] = df['future_return'] / df['future_volatility']\n", - " df['sharpe_ratio'].replace([np.inf, -np.inf], np.nan, inplace=True)\n", - "\n", - " return df['sharpe_ratio']\n", - "\n", - "\n", - "\n", - "def get_label(df):\n", - " # labels = df['future_af13'] - df['act_factor1']\n", - " # labels = df['future_close5']\n", - " # labels = df['future_af11']\n", - " # labels = df['ema_5'].shift(-1) - df['close']\n", - " # labels = df['future_af15']\n", - " df['label'] = calculate_risk_adjusted_target(df, days=5)\n", - " lower_percentile = df['label'].quantile(0.01) # 1%分位数\n", - " upper_percentile = df['label'].quantile(0.99) # 99%分位数\n", - " labels = df['label'].clip(lower=lower_percentile, upper=upper_percentile)\n", - " # labels = calculate_risk_adjusted_return(df, days=3, history_days=3, method='ratio')\n", - " return labels\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", - "# train_data = train_data.dropna(subset=feature_columns)\n", - "train_data = train_data.dropna(subset=feature_columns)\n", - "\n", - "train_data['label'] = get_label(train_data)\n", - "# test_data['label'] = get_label(test_data)\n", - "\n", - "# train_data = train_data.dropna(subset=['label'])\n", - "# test_data = test_data.dropna(subset=['label'])\n", - "# train_data = train_data.replace([np.inf, -np.inf], np.nan).dropna()\n", - "# test_data = test_data.replace([np.inf, -np.inf], np.nan).dropna()\n", - "\n", - "train_data = train_data.dropna(subset=['label'])\n", - "train_data = train_data.reset_index(drop=True)\n", - "\n", - "test_data = test_data.dropna(subset=feature_columns)\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" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1067937\n", - "最小日期: 2017-06-20\n", - "最大日期: 2022-11-29\n", - "403686\n", - "最小日期: 2023-01-03\n", - "最大日期: 2025-02-12\n" - ] - } - ], - "execution_count": 92 - }, - { - "cell_type": "code", - "id": "8f134d435f71e9e2", - "metadata": { - "jupyter": { - "source_hidden": true - }, - "ExecuteTime": { - "end_time": "2025-02-14T19:01:10.101871Z", - "start_time": "2025-02-14T19:01:10.011761Z" - } - }, - "source": [ - "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 objective(trial, X, y, num_boost_round, params):\n", - " # 参数网格\n", - " X, y = X.reset_index(drop=True), y.reset_index(drop=True)\n", - " param_grid = {\n", - " \"n_estimators\": trial.suggest_categorical(\"n_estimators\", [10000]),\n", - " \"learning_rate\": trial.suggest_float(\"learning_rate\", 0.01, 0.3),\n", - " \"num_leaves\": trial.suggest_int(\"num_leaves\", 20, 3000, step=25),\n", - " \"max_depth\": trial.suggest_int(\"max_depth\", 3, 16),\n", - " \"min_data_in_leaf\": trial.suggest_int(\"min_data_in_leaf\", 200, 10000, step=100),\n", - " \"lambda_l1\": trial.suggest_int(\"lambda_l1\", 0, 100, step=5),\n", - " \"lambda_l2\": trial.suggest_int(\"lambda_l2\", 0, 100, step=5),\n", - " \"min_gain_to_split\": trial.suggest_float(\"min_gain_to_split\", 0, 15),\n", - " \"bagging_fraction\": trial.suggest_float(\"bagging_fraction\", 0.2, 0.95, step=0.1),\n", - " \"bagging_freq\": trial.suggest_categorical(\"bagging_freq\", [1]),\n", - " \"feature_fraction\": trial.suggest_float(\"feature_fraction\", 0.2, 0.95, step=0.1),\n", - " \"random_state\": 1,\n", - " \"objective\": 'regression',\n", - " 'verbosity': -1\n", - " }\n", - " # 5折交叉验证\n", - " cv = KFold(n_splits=5, shuffle=False)\n", - "\n", - " cv_scores = np.empty(5)\n", - " for idx, (train_idx, test_idx) in enumerate(cv.split(X, y)):\n", - " X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]\n", - " y_train, y_test = y[train_idx], y[test_idx]\n", - "\n", - " # LGBM建模\n", - " model = lgb.LGBMRegressor(**param_grid, num_boost_round=num_boost_round)\n", - " model.fit(\n", - " X_train,\n", - " y_train,\n", - " eval_set=[(X_test, y_test)],\n", - " eval_metric=\"l2\",\n", - " callbacks=[\n", - " # LightGBMPruningCallback(trial, \"l2\"),\n", - " lgb.early_stopping(50, first_metric_only=True),\n", - " lgb.log_evaluation(period=-1)\n", - " ],\n", - " )\n", - " # 模型预测\n", - " preds = model.predict(X_test)\n", - " # 优化指标logloss最小\n", - " cv_scores[idx] = mean_absolute_error(y_test, preds)\n", - "\n", - " return np.mean(cv_scores)\n", - "\n", - "def generate_key(params, feature_columns, num_boost_round):\n", - " key_data = {\n", - " \"params\": params,\n", - " \"feature_columns\": feature_columns,\n", - " \"num_boost_round\": num_boost_round\n", - " }\n", - " # 转换成排序后的 JSON 字符串,再生成 md5 hash\n", - " key_str = json.dumps(key_data, sort_keys=True)\n", - " return hashlib.md5(key_str.encode('utf-8')).hexdigest()\n", - "\n", - "def train_light_model(df, params, feature_columns, callbacks, evals,\n", - " print_feature_importance=True, num_boost_round=100,\n", - " use_optuna=False):\n", - "\n", - " df_sorted = df.sort_values(by=['trade_date', 'label'], ascending=[True, False]) # 按日期升序、标签降序排序\n", - " df_sorted = df_sorted.sort_values(by='trade_date')\n", - " unique_dates = df_sorted['trade_date'].unique()\n", - " val_date_count = int(len(unique_dates) * 0.1)\n", - " val_dates = unique_dates[-val_date_count:]\n", - " val_indices = df_sorted[df_sorted['trade_date'].isin(val_dates)].index\n", - " train_indices = df_sorted[~df_sorted['trade_date'].isin(val_dates)].index\n", - "\n", - " # 获取训练集和验证集的样本\n", - " train_df = df_sorted.iloc[train_indices]\n", - " val_df = df_sorted.iloc[val_indices]\n", - "\n", - " X_train = train_df[feature_columns]\n", - " y_train = train_df['label']\n", - "\n", - " X_val = val_df[feature_columns]\n", - " y_val = val_df['label']\n", - "\n", - " train_data = lgb.Dataset(X_train, label=y_train)\n", - " val_data = lgb.Dataset(X_val, label=y_val)\n", - " if use_optuna:\n", - " # study = optuna.create_study(direction='minimize' if classify else 'maximize')\n", - " study = optuna.create_study(direction='minimize')\n", - " study.optimize(lambda trial: objective(trial, X_train, y_train, num_boost_round, params), n_trials=20)\n", - "\n", - " print(f\"Best parameters: {study.best_trial.params}\")\n", - " print(f\"Best score: {study.best_trial.value}\")\n", - "\n", - " params.update(study.best_trial.params)\n", - " model = lgb.train(\n", - " params, train_data, num_boost_round=num_boost_round,\n", - " valid_sets=[train_data, val_data], valid_names=['train', 'valid'],\n", - " callbacks=callbacks\n", - " )\n", - "\n", - " # 打印特征重要性(如果需要)\n", - " if print_feature_importance:\n", - " lgb.plot_metric(evals)\n", - " # lgb.plot_tree(model, figsize=(20, 8))\n", - " lgb.plot_importance(model, importance_type='split', max_num_features=20)\n", - " plt.show()\n", - " # with open(cache_file, 'wb') as f:\n", - " # pickle.dump({'key': cache_key,\n", - " # 'model': model,\n", - " # 'feature_columns': feature_columns}, f)\n", - " # print(\"模型训练完成并已保存缓存。\")\n", - " return model\n", - "\n", - "\n", - "from catboost import CatBoostRegressor\n", - "import pandas as pd\n", - "\n", - "\n", - "def train_catboost(df, feature_columns, params=None):\n", - " \"\"\"\n", - " 训练 CatBoost 排序模型\n", - " - df: 包含因子、date、instrument 和 label 的 DataFrame\n", - " - num_boost_round: 训练的轮数\n", - " - print_feature_importance: 是否打印特征重要性\n", - " - plot: 是否绘制特征重要性图\n", - " - split_date: 用于划分训练集和验证集的日期(比如 '2020-01-01')\n", - "\n", - " 返回训练好的模型\n", - " \"\"\"\n", - " df_sorted = df.sort_values(by=['trade_date', 'label'], ascending=[True, False])\n", - "\n", - " df_sorted = df_sorted.sort_values(by='trade_date')\n", - " unique_dates = df_sorted['trade_date'].unique()\n", - " val_date_count = int(len(unique_dates) * 0.1)\n", - " val_dates = unique_dates[-val_date_count:]\n", - " val_indices = df_sorted[df_sorted['trade_date'].isin(val_dates)].index\n", - " train_indices = df_sorted[~df_sorted['trade_date'].isin(val_dates)].index\n", - "\n", - " # 获取训练集和验证集的样本\n", - " train_df = df_sorted.iloc[train_indices].sort_values(by=['trade_date', 'label'], ascending=[True, False])\n", - " val_df = df_sorted.iloc[val_indices].sort_values(by=['trade_date', 'label'], ascending=[True, False])\n", - "\n", - " X_train = train_df[feature_columns]\n", - " y_train = train_df['label']\n", - "\n", - " X_val = val_df[feature_columns]\n", - " y_val = val_df['label']\n", - "\n", - " model = CatBoostRegressor(**params)\n", - " model.fit(X_train,\n", - " y_train,\n", - " eval_set=(X_val, y_val))\n", - "\n", - " return model" - ], - "outputs": [], - "execution_count": 93 - }, - { - "cell_type": "code", - "id": "4a4542e1ed6afe7d", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T19:02:42.420508Z", - "start_time": "2025-02-14T19:02:42.341401Z" - } - }, - "source": [ - "def max_drawdown_loss(y_true, y_pred):\n", - " # y_true和y_pred表示资产的实际和预测回报序列\n", - " cumulative_return = np.cumsum(y_pred) # 计算累积回报\n", - " peak = np.maximum.accumulate(cumulative_return)\n", - " drawdown = (cumulative_return - peak) / peak # 计算回撤\n", - " max_drawdown = np.min(drawdown) # 最大回撤\n", - "\n", - " loss = -max_drawdown # 最大回撤越大,损失越小,取负数使得回撤最小化\n", - " return loss, np.zeros_like(loss) # 返回损失和零梯度\n", - "\n", - "\n", - "\n", - "light_params = {\n", - " # 'objective': 'regression',\n", - " # 'metric': 'l2',\n", - " 'objective': 'quantile', # 分位回归\n", - " 'metric': 'quantile', # 使用 quantile 作为评估指标\n", - " 'alpha': 0.75, # 90% 分位数\n", - " 'learning_rate': 0.05,\n", - " 'is_unbalance': True,\n", - " 'num_leaves': 64,\n", - " 'min_data_in_leaf': 128,\n", - " 'max_depth': 6,\n", - " 'max_bin': 1024,\n", - " 'feature_fraction': 0.7,\n", - " 'bagging_fraction': 0.7,\n", - " 'bagging_freq': 5,\n", - " 'lambda_l1': 1,\n", - " 'lambda_l2': 1,\n", - " # 'boosting_type': 'dart',\n", - " 'verbosity': -1\n", - "}" - ], - "outputs": [], - "execution_count": 96 - }, - { - "cell_type": "code", - "id": "beeb098799ecfa6a", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T19:05:21.315576Z", - "start_time": "2025-02-14T19:02:42.469389Z" - } - }, - "source": [ - "print('train data size: ', len(train_data))\n", - "\n", - "evals = {}\n", - "light_model = train_light_model(train_data, light_params, feature_columns,\n", - " [lgb.log_evaluation(period=500),\n", - " lgb.callback.record_evaluation(evals),\n", - " lgb.early_stopping(50, first_metric_only=True)\n", - " ], evals,\n", - " num_boost_round=10000, use_optuna=False,\n", - " print_feature_importance=True)" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train data size: 1067937\n", - "Training until validation scores don't improve for 50 rounds\n", - "[500]\ttrain's quantile: 0.766393\tvalid's quantile: 0.783404\n", - "[1000]\ttrain's quantile: 0.749886\tvalid's quantile: 0.775331\n", - "[1500]\ttrain's quantile: 0.739319\tvalid's quantile: 0.771256\n", - "[2000]\ttrain's quantile: 0.731186\tvalid's quantile: 0.768522\n", - "[2500]\ttrain's quantile: 0.724016\tvalid's quantile: 0.766611\n", - "[3000]\ttrain's quantile: 0.717949\tvalid's quantile: 0.765331\n", - "[3500]\ttrain's quantile: 0.712718\tvalid's quantile: 0.76426\n", - "[4000]\ttrain's quantile: 0.708171\tvalid's quantile: 0.763329\n", - "[4500]\ttrain's quantile: 0.703619\tvalid's quantile: 0.762479\n", - "[5000]\ttrain's quantile: 0.699455\tvalid's quantile: 0.761843\n", - "[5500]\ttrain's quantile: 0.695762\tvalid's quantile: 0.761218\n", - "[6000]\ttrain's quantile: 0.692351\tvalid's quantile: 0.760606\n", - "[6500]\ttrain's quantile: 0.689176\tvalid's quantile: 0.760173\n", - "[7000]\ttrain's quantile: 0.686318\tvalid's quantile: 0.759704\n", - "Early stopping, best iteration is:\n", - "[7090]\ttrain's quantile: 0.685745\tvalid's quantile: 0.759607\n", - "Evaluated only: quantile\n" - ] - }, - { - "data": { - "text/plain": [ - "
" - ], - "image/png": "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tUwul9dq2+ZQFyJ7Y0AiauamT5+ON954A59++inefffdKtv5+PjA29sbv//+u831dbktQG3uq+Pr6wvAOFG3rry9vZGWlma13NYyhUJhNTkYMAY+Hx8fq+X1vSdQVby9vXHkyBGr5bb6Wh+2+rtr1y6kpqZiz549plEjAMjNzW2Q72wIjV0XInvjKTaiZq5169Z46aWXMHbsWDz++ONVthszZgyysrKg1+vRp08fq1enTp1MbWs7klCdjh07mk7/2Qow1Rk2bBjOnDmDkydPmi3/5ptvrNqGhobi77//Nlt24cIFnD9/vu6drochQ4YgPz8fv/32m9nyjRs31mp7uVwOAHWqd0Voqti2wmeffVbrfTS2260LUXPHESSiO8CSJUtqbPOPf/wDGzZswOjRozF37lz069cPUqkU169fx+7duzFu3DiMHz8eABAREYGNGzdi06ZNaNu2LRQKBSIiIurcr08++QRjx47FgAED8Pzzz6NNmzZISUnB9u3bsWHDhiq3mzdvHtauXYv7778fixYtgp+fHzZs2IBz585ZtZ06dSoee+wxzJkzBw8//DCuXr2K999/3zSC1dgef/xxLF++HI899hgWLVqE9u3b47fffsP27dsBAGJx9f+fGR4eDgD4/PPP4ebmBoVCgbCwMJunwCpER0fD09MTs2fPxptvvgmpVIoNGzZYBUp7ut26EDV3/A0maiEkEgl++ukn/Oc//8GWLVswfvx4PPjgg1iyZIlVAFq4cCGGDBmCJ598Ev369av1peOWRo4cib179yIgIADPPfcc7rvvPrz99ts1zkPx9/dHfHw8unbtin/961947LHHoFAo8PHHH1u1nTx5Mt5//31s374dY8aMwerVq7F69Wp07NixXn2uKxcXF+zatQtDhw7Fyy+/jIcffhgpKSlYtWoVAMDDw6Pa7cPCwhATE4OTJ09i6NCh6Nu3L37++edqt/H29savv/4KpVKJxx57DE888QRcXV2xadOmhjqs23a7dSFq7kSCIAj27gQR0Z3mvffew2uvvYaUlJR6TVRvqVgXail4io2IqAYVI1udO3eGTqfDrl27sHLlSjz22GMOHQJYF2rJGJCIiGqgVCqxfPlyJCcnQ6vVok2bNvj3v/+N1157zd5dsyvWhVoynmIjIiIissBJ2kREREQWGJCIiIiILDAgEREREVngJG0bDAYDUlNT4ebm1uCPLSAiIqLGIQgC8vPzERgYeNs3K2VAsiE1NRXBwcH27gYRERHVw7Vr1277VhMMSDZUPNTzypUr8PLysnNvmg+dTocdO3ZgxIgRkEql9u5Os8Ca2Ma6WGNNbGNdrLEmttWmLhqNBsHBwXV6OHdVGJBsqDit5ubmBnd3dzv3pvnQ6XRQKpVwd3fnf7TlWBPbWBdrrIltrIs11sS2utSlIabHcJI2ERERkQUGJCIiIiILDEhEREREFuwakPbu3YuxY8ciMDAQIpEIP/zwQ43bxMfHo3fv3lAoFGjbti0+/fRTqzabN29G165dIZfL0bVrV2zdurURek9ERFR3er0eJSUl1b6cnJxqbOOILycnJxgMhib5c7LrJO3CwkJ0794dM2bMwMMPP1xj+ytXrmD06NF48skn8d///hd//vkn5syZA19fX9P2Bw8exKRJk/DOO+9g/Pjx2Lp1KyZOnIj9+/ejf//+jX1IRERENgmCgLS0NOTm5tbYzt/fH9euXeO9+CqpqMuVK1fQtm1byGSyRv0+uwakUaNGYdSoUbVu/+mnn6JNmzaIiYkBAHTp0gV//fUXPvjgA1NAiomJwfDhw7FgwQIAwIIFCxAfH4+YmBh8++23DX4MREREtVERjlq1agWlUlll+DEYDCgoKICrq+tt3+ywJTEYDMjPz4dGo4FarUabNm0aNUDeUZf5Hzx4ECNGjDBbNnLkSKxZswY6nQ5SqRQHDx7E888/b9WmIlQRERE1Nb1ebwpH3t7e1bY1GAwoLS2FQqFgQKqkoi6+vr5Qq9UoKytr1Nsg3FEBKS0tDX5+fmbL/Pz8UFZWhszMTAQEBFTZJi0trcr9arVaaLVa02eNRgPAeM8FnU7XgEdwZ6uoBWtyC2tiG+tijTWxzVHqotVqIQgCFApFjXNoBEEw/Wyq+TZ3goq6ODk5QRAEs3+3KzTk79EdFZAA65s/VRSs8nJbbaobhlu8eDEWLlxotXz37t1QKpW3090WKS4uzt5daHZYE9tYF2usiW0tvS5OTk7w9/dHYWFhrf8Rz8/Pb+Re3ZmKiopQXFyM+Ph4lJWVWa1rKHdUQPL397caCcrIyICTk5NpyLKqNpajSpUtWLAA8+fPN32uuFX5sGHDahwKdSQ6nQ5xcXEYPnw47+5ajjWxjXWxxprY5ih1KSkpwbVr1+Dq6gqFQlFt24oHrvKB6eYq6uLi4gJnZ2cMHjzYqpYVZ4Aawh0VkKKiovDzzz+bLduxYwf69Olj+g8rKioKcXFxZvOQduzYgejo6Cr3K5fLIZfLrZZLpdIW/R9sfbEu1lgT21gXa6yJbS29Lnq9HiKRCGKxuMZ5RRWn1SratyShoaGYN28e5s2bV+dtK9dFJBLZ/J1pyN8huwakgoICXLp0yfT5ypUrSEhIgJeXF9q0aYMFCxbgxo0bWLduHQBg9uzZ+PjjjzF//nw8+eSTOHjwINasWWN2ddrcuXMxePBgLF26FOPGjcOPP/6InTt3Yv/+/U1+fERERHe6oUOHokePHg1ysdPRo0fh4uJy+51qAnaNpn/99Rd69uyJnj17AgDmz5+Pnj174o033gAAqNVqpKSkmNqHhYVh27Zt2LNnD3r06IF33nkHK1euNLuHUnR0NDZu3IivvvoKkZGRiI2NxaZNm3gPJCIiokYgCILVXKCq+Pr63jFze+0akIYOHQpBEKxesbGxAIDY2Fjs2bPHbJshQ4bg+PHj0Gq1uHLlCmbPnm213wkTJuDcuXMoLS3F2bNn8dBDDzXB0RAREbUs06dPR3x8PFasWGE6tRUbGwuRSITt27ejT58+kMvl2LdvHy5fvoxx48bBz88Prq6u6Nu3L3bu3Gm2v9DQULORKJFIhC+//BLjx4+HUqlEhw4d8NNPPzXxUdrWsk5uEhER3QEEQUBRaVmVr+JSfbXrb+dVcfV3baxYsQJRUVF48sknoVaroVarERwcDAB4+eWXsXjxYpw9exaRkZEoKCjA6NGjsXPnTpw4cQIjR47E2LFjzc4E2bJw4UJMnDgRf//9N0aPHo0pU6YgOzv7turbEO6oSdpEREQtQbFOj65vbLfLdye+PRJKWe3++VepVJDJZFAqlfD39wcAnDt3DgDw9ttvY/jw4aa23t7e6N69u+nzokWLsHXrVvz000945plnqvyO6dOn49FHHwUAvPfee/joo49w5MgR3HfffXU+tobEESQiIiKqsz59+ph9LiwsxMsvv4yuXbvCw8MDrq6uOHfuXI0jSJGRkab3Li4ucHNzQ0ZGRqP0uS44gkRERNTEnKUSJL490uY6g8GAfE0+3NzdGuUyf2eppEH2Y3k12ksvvYTt27fjgw8+QPv27eHs7IwJEyagtLS02v1YXpovEomaxR3EGZCIiIiamEgkqvI0l8FgQJlMAqXMqVncB0kmk0Gv19fYbt++fZg+fTrGjx8PwHgrn+Tk5EbuXeOxf+WJiIio2QoNDcXhw4eRnJyMzMzMKkd32rdvjy1btiAhIQEnT57E5MmTm8VIUH0xIBEREVGVXnzxRUgkEnTt2hW+vr5Vzilavnw5PD09ER0djbFjx2LkyJHo1atXE/e24fAUGxEREVWpY8eOOHjwoNmy6dOnW7ULDQ3Frl27zJY9/fTTZp8tT7nZuuVAbm5uvfrZ0DiCRERERGSBAYmIiIjIAgMSERERkQUGJCIiIiILDEhEREREFhiQiIiIiCwwIBERERFZYEAiIiIissCARERERGSBAYmIiIgaTWhoKGJiYkyfRSIRfvjhhyrbJycnQyQSISEhodH7Vh0+aoSIiIiajFqthqenp727USMGJCIiImoy/v7+9u5CrfAUGxEREdn02WefoXXr1jAYDGbLH3jgATz++OO4fPkyxo0bBz8/P7i6uqJv377YuXNntfu0PMV25MgR9OzZEwqFAn369MGJEyca41DqjAGJiIioqQkCUFpY9UtXVP3623kJQq27+cgjjyAzMxO7d+82LcvJycH27dsxZcoUFBQUYPTo0di5cydOnDiBkSNHYuzYsUhJSanV/gsLCzFmzBh06tQJx44dw1tvvYUXX3yxzuVsDDzFRkRE1NR0RcB7gTZXiQF4NOZ3/ycVkLnUqqmXlxfuu+8+fPPNN7jnnnsAAN999x28vLxwzz33QCKRoHv37qb2ixYtwtatW/HTTz/hmWeeqXH/GzZsgF6vx9q1a6FUKtGtWzdcv34d//rXv+p3bA2II0hERERUpSlTpmDz5s3QarUAjKHmH//4ByQSCQoLC/Hyyy+ja9eu8PDwgKurK86dO1frEaSzZ8+ie/fuUCqVpmVRUVGNchx1xREkIiKipiZVGkdybDAYDNDk58PdzQ1icSOMY0iVNbepZOzYsTAYDPj111/Rt29f7Nu3D8uWLQMAvPTSS9i+fTs++OADtG/fHs7OzpgwYQJKS0trtW+hDqf7mhoDEhERUVMTiao+zWUwAFK9cX1jBKQ6cnZ2xkMPPYQNGzbg0qVL6NixI3r37g0A2LdvH6ZPn47x48cDAAoKCpCcnFzrfXft2hXr169HcXExnJ2dAQCHDh1q8GOoD/tXnoiIiJq1KVOm4Ndff8XatWvx2GOPmZa3b98eW7ZsQUJCAk6ePInJkydbXfFWncmTJ0MsFmPmzJlITEzEtm3b8MEHHzTGIdQZAxIRERFV6+6774aXlxfOnz+PyZMnm5YvX74cnp6eiI6OxtixYzFy5Ej06tWr1vt1dXXFzz//jMTERPTs2ROvvvoqli5d2hiHUGc8xUZERETVkkgkSE21njMVGhqKXbt2mS17+umnzT5bnnKznHc0YMAAq8eKNIe5SRxBIiIiIrLAgERERERkgQGJiIiIyAIDEhEREZEFBiQiIqIm0hwmH9/pmqqGDEjVKS20dw+IiKgFkEqlAICioiI79+TOp9PpABivrGtMvMy/GqLk/UDA5JobEhERVUMikcDDwwMZGRkAAKVSCZFIZLOtwWBAaWkpSkpKGudRI3cog8EArVYLjUYDpVIJJ6fGjTAMSNWq/d1AiYiIquPv7w8AppBUFUEQTI/eqCpEOaKKuri4uCAgIKDRa8OAVB2BAYmIiBqGSCRCQEAAWrVqZTpNZItOp8PevXsxePBg06k5MtYlPj4ew4cPh0wma/Tvs3tAWrVqFf7v//4ParUa3bp1Q0xMDAYNGlRl+08++QQff/wxkpOT0aZNG7z66quYNm2aaX1sbCxmzJhhtV1xcTEUCkWd+lZSWvUvMBERUX1IJJJq589IJBKUlZVBoVAwIFUikUig1+ub7LSjXQPSpk2bMG/ePKxatQoDBw7EZ599hlGjRiExMRFt2rSxar969WosWLAAX3zxBfr27YsjR47gySefhKenJ8aOHWtq5+7ujvPnz5ttW9dwBACXM/IRWPfDIiIiojucXQPSsmXLMHPmTMyaNQsAEBMTg+3bt2P16tVYvHixVfv169fjqaeewqRJkwAAbdu2xaFDh7B06VKzgCQSiUznem8HL8ckIiJyTHYLSKWlpTh27BheeeUVs+UjRozAgQMHbG6j1WqtRoKcnZ1x5MgR6HQ601BkQUEBQkJCoNfr0aNHD7zzzjvo2bNnlX3RarXQarWmzxqNBgCg1+uqPU/saCpqwZrcwprYxrpYY01sY12ssSa21aYuDVkzuwWkzMxM6PV6+Pn5mS338/NDWlqazW1GjhyJL7/8Eg8++CB69eqFY8eOYe3atdDpdMjMzERAQAA6d+6M2NhYREREQKPRYMWKFRg4cCBOnjyJDh062Nzv4sWLsXDhQqvlN67fwLZt227/YFuYuLg4e3eh2WFNbGNdrLEmtrEu1lgT26qrS0PeZ8ruk7QtL9MTBKHKS/def/11pKWlYcCAARAEAX5+fpg+fTref/9904S3AQMGYMCAAaZtBg4ciF69euGjjz7CypUrbe53wYIFmD9/vumzRqNBcHAwAgMDMGj06Ns9xBZDp9MhLi4Ow4cP58TBcqyJbayLNdbENtbFGmtiW23qUnEGqCHYLSD5+PhAIpFYjRZlZGRYjSpVcHZ2xtq1a/HZZ58hPT0dAQEB+Pzzz+Hm5gYfHx+b24jFYvTt2xcXL16ssi9yuRxyudx6W4j4y2mDVCplXSywJraxLtZYE9tYF2usiW3V1aUh62W3W3TKZDL07t3baqgsLi4O0dHR1W4rlUoRFBQEiUSCjRs3YsyYMVVe9icIAhISEhAQEFD3Tgr6um9DREREdzy7nmKbP38+pk6dij59+iAqKgqff/45UlJSMHv2bADGU183btzAunXrAAAXLlzAkSNH0L9/f+Tk5GDZsmU4ffo0vv76a9M+Fy5ciAEDBqBDhw7QaDRYuXIlEhIS8Mknn9S5fwJvFElEROSQ7BqQJk2ahKysLLz99ttQq9UIDw/Htm3bEBISAgBQq9VISUkxtdfr9fjwww9x/vx5SKVSDBs2DAcOHEBoaKipTW5uLv75z38iLS0NKpUKPXv2xN69e9GvX7+6d5ABiYiIyCHZfZL2nDlzMGfOHJvrYmNjzT536dIFJ06cqHZ/y5cvx/LlyxumcwxIREREDomPCa6OgTeKJCIickQMSNXhCBIREZFDYkCqBidpExEROSYGpOowIBERETkkBqRqcQ4SERGRI2JAqg5HkIiIiBwSA1J1GJCIiIgcEgNSdRiQiIiIHBIDUnUYkIiIiBwSA1J1GJCIiIgcEgNSdQRexUZEROSIGJCqwxEkIiIih8SAVB0GJCIiIofEgFQdnmIjIiJySAxI1eEIEhERkUNiQKoOAxIREZFDYkCqDgMSERGRQ2JAqg7nIBERETkkBqRqcQSJiIjIETEgVYcjSERERA6JAak6nINERETkkBiQqiHiCBIREZFDYkCqFkeQiIiIHBEDUnV4io2IiMghMSBVQwSeYiMiInJEDEjV4QgSERGRQ2JAqhZHkIiIiBwRA1I1RBxBIiIickgMSNVhQCIiInJIDEjV4H2QiIiIHBMDUrU4gkREROSIGJCqwTlIREREjokBqTo8xUZEROSQGJCqIeIpNiIiIofEgFQdnmIjIiJySAxI1RDZuwNERERkFwxI1eAkbSIiIsfEgFQNBiQiIiLHZPeAtGrVKoSFhUGhUKB3797Yt29fte0/+eQTdOnSBc7OzujUqRPWrVtn1Wbz5s3o2rUr5HI5unbtiq1bt9azd7yKjYiIyBHZNSBt2rQJ8+bNw6uvvooTJ05g0KBBGDVqFFJSUmy2X716NRYsWIC33noLZ86cwcKFC/H000/j559/NrU5ePAgJk2ahKlTp+LkyZOYOnUqJk6ciMOHD9e5fxxBIiIickx2DUjLli3DzJkzMWvWLHTp0gUxMTEIDg7G6tWrbbZfv349nnrqKUyaNAlt27bFP/7xD8ycORNLly41tYmJicHw4cOxYMECdO7cGQsWLMA999yDmJiYOvePl/kTERE5Jid7fXFpaSmOHTuGV155xWz5iBEjcODAAZvbaLVaKBQKs2XOzs44cuQIdDodpFIpDh48iOeff96szciRI6sNSFqtFlqt1vRZo9EAMI4g6XS6uhxWi1ZRC9bkFtbENtbFGmtiG+tijTWxrTZ1acia2S0gZWZmQq/Xw8/Pz2y5n58f0tLSbG4zcuRIfPnll3jwwQfRq1cvHDt2DGvXroVOp0NmZiYCAgKQlpZWp30CwOLFi7Fw4UKr5frSUmzbtq0eR9eyxcXF2bsLzQ5rYhvrYo01sY11scaa2FZdXYqKihrse+wWkCqIROZ3GxIEwWpZhddffx1paWkYMGAABEGAn58fpk+fjvfffx8SiaRe+wSABQsWYP78+abPGo0GwcHBkDuJMHr06PocVouk0+kQFxeH4cOHQyqV2rs7zQJrYhvrYo01sY11scaa2FabulScAWoIdgtIPj4+kEgkViM7GRkZViNAFZydnbF27Vp89tlnSE9PR0BAAD7//HO4ubnBx8cHAODv71+nfQKAXC6HXC63Wi6CwF9OG6RSKetigTWxjXWxxprYxrpYY01sq64uDVkvu03Slslk6N27t9VQWVxcHKKjo6vdViqVIigoCBKJBBs3bsSYMWMgFhsPJSoqymqfO3bsqHGftoigr/M2REREdOez6ym2+fPnY+rUqejTpw+ioqLw+eefIyUlBbNnzwZgPPV148YN072OLly4gCNHjqB///7IycnBsmXLcPr0aXz99demfc6dOxeDBw/G0qVLMW7cOPz444/YuXMn9u/fX+f+iQUGJCIiIkdk14A0adIkZGVl4e2334ZarUZ4eDi2bduGkJAQAIBarTa7J5Jer8eHH36I8+fPQyqVYtiwYThw4ABCQ0NNbaKjo7Fx40a89tpreP3119GuXTts2rQJ/fv3r3P/eB8kIiIix2T3Sdpz5szBnDlzbK6LjY01+9ylSxecOHGixn1OmDABEyZMuO2+iXmKjYiIyCHZ/VEjzZmYI0hEREQOiQGpGmLeSZuIiMghMSBVgyNIREREjokBqRqcg0REROSYGJCqIeEpNiIiIofEgFQN3iiSiIjIMTEgVYMjSERERI6JAakavIqNiIjIMTEgVYMBiYiIyDExIFVDwjlIREREDokBqRpiCPbuAhEREdkBA1I1pBxBIiIickgMSDUxcB4SERGRo2FAqonAUSQiIiJHw4BUE0OZvXtARERETYwBqQYCAxIREZHDYUCqgUHPgERERORoGJBqoC/jHCQiIiJHw4BUA32Zzt5dICIioibGgFQDA+cgERERORwGpBrwFBsREZHjYUCqgUHPU2xERESOhgGpBnpexUZERORwGJBqwMv8iYiIHA8DUg04SZuIiMjxMCDVwMBJ2kRERA6HAakGnKRNRETkeBiQasA5SERERI6HAakGBj1PsRERETkaBqQaCPpSe3eBiIiImhgDUg0MZQxIREREjoYBqQYCJ2kTERE5HAakGghlWnt3gYiIiJoYA1IN9GUcQSIiInI0DEg1EDgHiYiIyOEwINWAk7SJiIgcDwNSDThJm4iIyPHYPSCtWrUKYWFhUCgU6N27N/bt21dt+w0bNqB79+5QKpUICAjAjBkzkJWVZVofGxsLkUhk9SopKalX/wycpE1ERORw7BqQNm3ahHnz5uHVV1/FiRMnMGjQIIwaNQopKSk22+/fvx/Tpk3DzJkzcebMGXz33Xc4evQoZs2aZdbO3d0darXa7KVQKOrVR44gEREROR67BqRly5Zh5syZmDVrFrp06YKYmBgEBwdj9erVNtsfOnQIoaGheO655xAWFoa77roLTz31FP766y+zdiKRCP7+/mav+hJ4FRsREZHDsVtAKi0txbFjxzBixAiz5SNGjMCBAwdsbhMdHY3r169j27ZtEAQB6enp+P7773H//febtSso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" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ], - "image/png": "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" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "execution_count": 97 - }, - { - "cell_type": "code", - "id": "63235069-dc59-48fb-961a-e80373e41a61", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T19:05:21.442954Z", - "start_time": "2025-02-14T19:05:21.364837Z" - } - }, - "source": [ - "print('train data size: ', len(train_data))\n", - "\n", - "catboost_params = {\n", - " 'loss_function': 'MAE', # 90% 分位回归\n", - " 'iterations': 5000, # 训练轮数\n", - " 'learning_rate': 0.05, # 学习率,较低以防止过拟合\n", - " 'depth': 16, # 树的深度,防止过拟合\n", - " 'l2_leaf_reg': 10.0, # L2 正则化,提高泛化能力\n", - " 'bagging_temperature': 1, # 降低过拟合\n", - " # 'subsample': 0.8, # 每轮随机 80% 的样本,减少过拟合\n", - " 'colsample_bylevel': 0.8, # 每层 80% 特征子集,防止过拟合\n", - " 'random_seed': 42, # 固定随机种子,保证可复现\n", - " 'verbose': 500, # 每 100 轮打印一次信息\n", - " 'early_stopping_rounds': 100, # 早停,防止过拟合\n", - " # 'task_type': 'GPU'\n", - "}\n", - "\n", - "# catboost_model = train_catboost(train_data, feature_columns, catboost_params)" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train data size: 1067937\n" - ] - } - ], - "execution_count": 98 - }, - { - "cell_type": "code", - "id": "5bb96ca8492e74d", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T19:05:44.296879Z", - "start_time": "2025-02-14T19:05:21.450842Z" - } - }, - "source": [ - "score_df = test_data\n", - "score_df['score'] = light_model.predict(score_df[feature_columns])\n", - "# train_data['score'] = catboost_model.predict(train_data[feature_columns])\n", - "predictions_test = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", - "predictions_test = predictions_test[predictions_test['score'] > 0]\n", - "predictions_test[['trade_date', 'score', 'ts_code']].to_csv('predictions_test.tsv', index=False)" - ], - "outputs": [], - "execution_count": 99 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T19:06:45.153554Z", - "start_time": "2025-02-14T19:05:44.329062Z" - } - }, - "cell_type": "code", - "source": [ - "score_df = train_data\n", - "score_df['score'] = light_model.predict(score_df[feature_columns])\n", - "# train_data['score'] = catboost_model.predict(train_data[feature_columns])\n", - "predictions_test = score_df.loc[score_df.groupby('trade_date')['score'].idxmax()]\n", - "predictions_test = predictions_test[predictions_test['score'] > 0]\n", - "predictions_test[['trade_date', 'score', 'ts_code']].to_csv('predictions_train.tsv', index=False)" - ], - "id": "7359f89064a124d2", - "outputs": [], - "execution_count": 100 - }, - { - "cell_type": "code", - "id": "b427ce41-9739-4e9e-bea8-5f2551fec5d7", - "metadata": { - "ExecuteTime": { - "end_time": "2025-02-14T19:06:45.232334Z", - "start_time": "2025-02-14T19:06:45.218159Z" - } - }, - "source": [], - "outputs": [], - "execution_count": null - } - ], - "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.8.19" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/code/train/utils/utils.py b/code/train/utils/utils.py index f84e850..87ccaa6 100644 --- a/code/train/utils/utils.py +++ b/code/train/utils/utils.py @@ -1,21 +1,8 @@ import numpy as np import pandas as pd -def read_and_merge_h5_data(h5_filename, key, columns, df=None): - """ - 读取 HDF5 文件中的数据,根据指定的 columns 筛选数据, - 如果传入 df 参数,则将其与读取的数据根据 ts_code 和 trade_date 合并。 - 参数: - - h5_filename: HDF5 文件名 - - key: 数据存储在 HDF5 文件中的 key - - columns: 要读取的列名列表 - - df: 需要合并的 DataFrame(如果为空,则不进行合并) - - 返回: - - 合并后的 DataFrame - """ - # 处理 _ 开头的列名 +def read_and_merge_h5_data(h5_filename, key, columns, df=None, join='left', on=['ts_code', 'trade_date'], prefix=None): processed_columns = [] for col in columns: if col.startswith('_'): @@ -32,14 +19,22 @@ def read_and_merge_h5_data(h5_filename, key, columns, df=None): new_col = f'_{col}' data.rename(columns={col: new_col}, inplace=True) + if prefix is not None: + for col in data.columns: + if col not in ['ts_code', 'trade_date']: # 只有不在 columns 中的列才需要加下划线 + new_col = f'{prefix}_{col}' + data.rename(columns={col: new_col}, inplace=True) + # 如果传入的 df 不为空,则进行合并 if df is not None and not df.empty: - # 确保两个 DataFrame 都有 ts_code 和 trade_date 列 - df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d') - data['trade_date'] = pd.to_datetime(data['trade_date'], format='%Y%m%d') + print(f'{join} merge on {on}') + if 'trade_date' in on: + # 确保两个 DataFrame 都有 ts_code 和 trade_date 列 + df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d') + data['trade_date'] = pd.to_datetime(data['trade_date'], format='%Y%m%d') # 根据 ts_code 和 trade_date 合并 - merged_df = pd.merge(df, data, on=['ts_code', 'trade_date'], how='left') + merged_df = pd.merge(df, data, on=on, how=join) else: # 如果 df 为空,则直接返回读取的数据 merged_df = data @@ -84,4 +79,42 @@ def calculate_risk_adjusted_return(df, days=1, method='ratio', lambda_=0.5, eps= else: raise ValueError("Invalid method. Use 'ratio' or 'difference'.") - return df \ No newline at end of file + 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']): +# processed_columns = [] +# for col in columns: +# if col.startswith('_'): +# processed_columns.append(col[1:]) # 去掉下划线 +# else: +# processed_columns.append(col) +# +# # 从 HDF5 文件读取数据,选择需要的列 +# pd_df = pd.read_hdf(h5_filename, key=key, columns=processed_columns) +# +# # 将 Pandas DataFrame 转换为 Polars DataFrame +# data = pl.from_pandas(pd_df) +# +# # 修改列名,如果列名以前有 _,加上 _ +# data = data.rename({col: f'_{col}' for col in data.columns if col not in columns}) +# +# # 如果传入的 df 不为空,则进行合并 +# if df is not None and not df.is_empty(): +# print(f'{join} merge on {on}') +# +# # 确保两个 DataFrame 都有 ts_code 和 trade_date 列 +# # df = df.with_columns(pl.col('trade_date').str.strptime(pl.Datetime, format='%Y%m%d')) +# # data = data.with_columns(pl.col('trade_date').str.strptime(pl.Datetime, format='%Y%m%d')) +# +# # 根据 ts_code 和 trade_date 合并 +# merged_df = df.join(data, on=on, how=join) +# else: +# # 如果 df 为空,则直接返回读取的数据 +# merged_df = data +# +# return merged_df + +