From c711a8c94a9938f57bc2426c30f9386613bcd216 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Fri, 17 Jan 2025 18:20:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=98=8E=E7=A1=AE=E7=AD=96=E7=95=A5=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E6=96=B9=E5=90=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/codeStyles/codeStyleConfig.xml | 5 + selector.py | 20 - strategy.py | 44 ++ 财报筛选/选股测试.ipynb | 2 +- 财报筛选/金字塔选股.ipynb | 853 +++++++++++++++++++++------ 5 files changed, 731 insertions(+), 193 deletions(-) create mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 selector.py create mode 100644 strategy.py diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/selector.py b/selector.py deleted file mode 100644 index edef7f9..0000000 --- a/selector.py +++ /dev/null @@ -1,20 +0,0 @@ -import pandas as pd - - -class Selector: - def select(self, codes: [str], df: pd.DataFrame) -> [str]: - return codes - - -class Score: - def score(self, codes: [str], df: pd.DataFrame) -> [(str, int)]: - return list(map(lambda code: (code, 0), codes)) - - -class PeriodSelector(Selector): - def __init__(self, period: int = 5): - self.__period = period - - def select(self, codes: [str], df: pd.DataFrame) -> [str]: - size_df = df.groupby("code").size() - return list(filter(lambda code: size_df[code] > self.__period, codes)) diff --git a/strategy.py b/strategy.py new file mode 100644 index 0000000..38572b7 --- /dev/null +++ b/strategy.py @@ -0,0 +1,44 @@ +import pandas as pd + + +class Selector: + def select(self, codes: [str], df: pd.DataFrame) -> [str]: + return codes + + +class Strategy: + def __init__(self, selectors: [Selector]): + self.selectors = selectors + + def select(self, codes: [str], df: pd.DataFrame) -> [str]: + return list(map(lambda code: self.selectors.select(code, df), codes)) + + +class PeriodSelector(Selector): + def __init__(self, period: int = 5): + self.__period = period + + def select(self, codes: [str], df: pd.DataFrame) -> [str]: + size_df = df.groupby("code").size() + return list(filter(lambda code: size_df[code] > self.__period, codes)) + + +class PyramidSelector(Selector): + def select(self, codes: [str], df: pd.DataFrame) -> [str]: + target_df = df[df["code"].isin(codes)] + target_df["score"] = 0 + group_df = target_df.groupby("code") + + target_df["prev_total_stockholder_interest"] = group_df["total_stockholder_interest"].shift(1) + target_df["roe"] = target_df["net_income"] / ((target_df["prev_total_stockholder_interest"] + target_df["total_stockholder_interest"]) / 2) + target_df["average_roe"] = target_df["roe"].mean() + target_df[target_df["average_roe"] >= 35] = target_df["score"] + 550 + target_df[(target_df["average_roe"] < 35) & (target_df["average_roe"] >= 30)] = target_df["score"] + 500 + target_df[(target_df["average_roe"] < 30) & (target_df["average_roe"] >= 25)] = target_df["score"] + 450 + target_df[(target_df["average_roe"] < 25) & (target_df["average_roe"] >= 15)] = target_df["score"] + 300 + target_df[(target_df["average_roe"] < 15) & (target_df["average_roe"] >= 10)] = target_df["score"] + 250 + + target_df["prev_total_assets"] = group_df["total_assets"].shift(1) + target_df["roa"] = target_df["net_income"] / ((target_df["prev_total_assets"] + target_df["total_assets"]) / 2) + + return super().select(codes, df) diff --git a/财报筛选/选股测试.ipynb b/财报筛选/选股测试.ipynb index a1ae465..9dcc632 100644 --- a/财报筛选/选股测试.ipynb +++ b/财报筛选/选股测试.ipynb @@ -67,7 +67,7 @@ }, "cell_type": "code", "source": [ - "from selector import PeriodSelector\n", + "from strategy import PeriodSelector\n", "\n", "filter_df = finance_df[(2010 < finance_df[\"year\"]) & (finance_df[\"year\"] < 2025)]\n", "codes = filter_df[\"code\"].unique().tolist()\n", diff --git a/财报筛选/金字塔选股.ipynb b/财报筛选/金字塔选股.ipynb index 68f1bd4..0bd1be2 100644 --- a/财报筛选/金字塔选股.ipynb +++ b/财报筛选/金字塔选股.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-01-14T14:10:53.801418Z", - "start_time": "2025-01-14T14:10:53.532671Z" + "end_time": "2025-01-17T02:07:55.001963Z", + "start_time": "2025-01-17T02:07:54.772694Z" } }, "source": [ @@ -22,8 +22,103 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-01-14T14:10:56.411909Z", - "start_time": "2025-01-14T14:10:55.020264Z" + "end_time": "2025-01-17T08:18:12.685428Z", + "start_time": "2025-01-17T08:18:12.676949Z" + } + }, + "cell_type": "code", + "source": [ + "df = pd.DataFrame([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]])\n", + "df.loc[df[0] > 2, 2] += 100\n", + "df" + ], + "id": "e19fd115ed8cb631", + "outputs": [ + { + "data": { + "text/plain": [ + " 0 1 2 3 4\n", + "0 1 2 3 4 5\n", + "1 6 7 108 9 10\n", + "2 11 12 113 14 15\n", + "3 16 17 118 19 20" + ], + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
01234
012345
167108910
211121131415
316171181920
\n", + "
" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 86 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-17T10:11:56.563929Z", + "start_time": "2025-01-17T10:11:52.872684Z" } }, "cell_type": "code", @@ -72,46 +167,192 @@ " \"operate_profit\",\n", " \"money_cap\",\n", " \"n_cashflow_act\",\n", - "]]" + "]]\n", + "finance_df[\"score\"] = 0\n", + "finance_df = finance_df.sort_values(by=[\"code\", \"year\"], ascending=True)" ], "id": "68b2debc14502fd5", "outputs": [], - "execution_count": 3 + "execution_count": 148 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "过滤上市时间大于5年的企业,由于计算很多数值需要与前一年比,所以实际上需要的是上市时间至少6年的企业", + "id": "3052b8c4442f3643" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-01-14T14:22:17.070395Z", - "start_time": "2025-01-14T14:22:17.053180Z" + "end_time": "2025-01-17T10:11:57.531470Z", + "start_time": "2025-01-17T10:11:57.287352Z" } }, "cell_type": "code", - "source": "finance_df[finance_df[\"code\"] == \"600763.SH\"][[\"year\", \"net_income\"]]", - "id": "f0e8942b3403348c", + "source": "finance_df = finance_df.groupby(\"code\").filter(lambda x: len(x) > 6)", + "id": "4293bd93ea8f9ed", + "outputs": [], + "execution_count": 149 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "ROE\n", + "\n", + "股东权益报酬率(%) RoE\t股东权益报酬率RoE 是判断股票是否具备”长期上涨潜力“最重要的指标之一, RoE 如果既稳定又优秀,那这家公司的其他指标也基本不会差。 \n", + "RoE均值 >= 35\t550分 \n", + "35 > RoE均值 >= 30\t500分 \n", + "30 > RoE均值 >= 25\t450分 \n", + "25 > RoE均值 >= 20\t400分 \n", + "20 > RoE均值 >= 15\t300分 \n", + "15 > RoE均值 >= 10\t250分 \n", + "10 > RoE均值 或 0 >= 其中一年\t0分 " + ], + "id": "336aa970ee46709d" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-17T10:12:05.859920Z", + "start_time": "2025-01-17T10:12:00.853021Z" + } + }, + "cell_type": "code", + "source": [ + "finance_df[\"prev_total_stockholder_interest\"] = finance_df.groupby(\"code\")[\"total_stockholder_interest\"].shift(1)\n", + "finance_df[\"roe\"] = finance_df[\"net_income\"] / (\n", + " (finance_df[\"prev_total_stockholder_interest\"] + finance_df[\"total_stockholder_interest\"]) / 2)\n", + "finance_df[\"average_roe\"] = finance_df.groupby(\"code\")[\"roe\"].rolling(window=5).mean().reset_index(0, drop=True)\n", + "finance_df.loc[finance_df[\"average_roe\"] >= 0.35, \"score\"] += 550\n", + "finance_df.loc[(finance_df[\"average_roe\"] >= 0.30) & (finance_df[\"average_roe\"] < 0.35), \"score\"] += 500\n", + "finance_df.loc[(finance_df[\"average_roe\"] >= 0.25) & (finance_df[\"average_roe\"] < 0.30), \"score\"] += 450\n", + "finance_df.loc[(finance_df[\"average_roe\"] >= 0.20) & (finance_df[\"average_roe\"] < 0.25), \"score\"] += 400\n", + "finance_df.loc[(finance_df[\"average_roe\"] >= 0.15) & (finance_df[\"average_roe\"] < 0.20), \"score\"] += 300\n", + "finance_df.loc[(finance_df[\"average_roe\"] >= 0.10) & (finance_df[\"average_roe\"] < 0.15), \"score\"] += 250\n", + "finance_df[\"score\"] = finance_df.groupby(\"code\").apply(\n", + " lambda x: x[\"score\"].mask(x[\"average_roe\"].rolling(window=5).apply(lambda y: (y < 0).any(), raw=False) > 0, 0)\n", + ").reset_index(drop=True)\n", + "\n", + "finance_df[finance_df[\"code\"] == '000002.SZ']" + ], + "id": "f050d33c4a0cd720", "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/7h/w0cmp4zj6mn9br_6nyj310m40000gn/T/ipykernel_54636/2126828454.py:12: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.\n", + " finance_df[\"score\"] = finance_df.groupby(\"code\").apply(\n" + ] + }, { "data": { "text/plain": [ - " year net_income\n", - "1808 2005 3.467468e+06\n", - "3228 2006 1.212677e+06\n", - "4784 2007 1.015147e+07\n", - "8425 2008 1.466373e+07\n", - "9811 2009 2.172666e+07\n", - "12426 2010 4.982651e+07\n", - "17669 2011 7.169039e+07\n", - "19876 2012 9.554584e+07\n", - "25082 2013 1.024329e+08\n", - "28480 2014 1.138340e+08\n", - "33116 2015 1.936414e+08\n", - "37622 2016 1.328421e+08\n", - "41906 2017 2.266875e+08\n", - "47465 2018 3.592014e+08\n", - "51158 2019 5.077704e+08\n", - "54614 2020 5.449616e+08\n", - "61814 2021 7.861501e+08\n", - "66356 2022 6.156781e+08\n", - "71478 2023 5.784234e+08" + " code year total_stockholder_interest net_income \\\n", + "1945 000002.SZ 2005 8.581223e+09 1.433426e+09 \n", + "3432 000002.SZ 2006 1.745350e+10 2.422997e+09 \n", + "5449 000002.SZ 2007 3.391952e+10 5.317501e+09 \n", + "7921 000002.SZ 2008 3.881855e+10 4.639869e+09 \n", + "10605 000002.SZ 2009 4.540851e+10 6.430008e+09 \n", + "13934 000002.SZ 2010 5.458620e+10 8.839611e+09 \n", + "17289 000002.SZ 2011 6.783254e+10 1.159961e+10 \n", + "20376 000002.SZ 2012 8.213819e+10 1.566259e+10 \n", + "25215 000002.SZ 2013 1.054394e+11 1.829755e+10 \n", + "29387 000002.SZ 2014 1.158936e+11 1.928752e+10 \n", + "33753 000002.SZ 2015 1.363096e+11 2.594944e+10 \n", + "37463 000002.SZ 2016 1.616766e+11 2.835026e+10 \n", + "42678 000002.SZ 2017 1.866739e+11 3.720839e+10 \n", + "47756 000002.SZ 2018 2.356207e+11 4.927229e+10 \n", + "52944 000002.SZ 2019 2.705791e+11 5.513161e+10 \n", + "57414 000002.SZ 2020 3.498445e+11 5.929812e+10 \n", + "59180 000002.SZ 2021 3.927728e+11 3.806953e+10 \n", + "62929 000002.SZ 2022 4.049915e+11 3.755091e+10 \n", + "68422 000002.SZ 2023 4.029335e+11 2.045556e+10 \n", + "\n", + " total_assets total_revenue inventories accounts_receivable \\\n", + "1945 2.199239e+10 1.055885e+10 1.484948e+10 3.773077e+08 \n", + "3432 4.991984e+10 1.791833e+10 3.416711e+10 3.646097e+08 \n", + "5449 1.000945e+11 3.552661e+10 6.647288e+10 8.648830e+08 \n", + "7921 1.192366e+11 4.099178e+10 8.589870e+10 9.227748e+08 \n", + "10605 1.376086e+11 4.888101e+10 9.008529e+10 7.131919e+08 \n", + "13934 2.156376e+11 5.071385e+10 1.333335e+11 1.594025e+09 \n", + "17289 2.962084e+11 7.178275e+10 2.083355e+11 1.514814e+09 \n", + "20376 3.788016e+11 1.031162e+11 2.551641e+11 1.886549e+09 \n", + "25215 4.792053e+11 1.354188e+11 3.311332e+11 3.078970e+09 \n", + "29387 5.084088e+11 1.463880e+11 3.177264e+11 1.894072e+09 \n", + "33753 6.112956e+11 1.955491e+11 3.681219e+11 2.510653e+09 \n", + "37463 8.306742e+11 2.404772e+11 4.673613e+11 2.075257e+09 \n", + "42678 1.165347e+12 2.428971e+11 5.980877e+11 1.432734e+09 \n", + "47756 1.528579e+12 2.976793e+11 7.503026e+11 1.586181e+09 \n", + "52944 1.729929e+12 3.678939e+11 8.970190e+11 1.988076e+09 \n", + "57414 1.869177e+12 4.191117e+11 1.002063e+12 2.992423e+09 \n", + "59180 1.938638e+12 4.527978e+11 1.075617e+12 4.743597e+09 \n", + "62929 1.757124e+12 5.038384e+11 9.070569e+11 7.504692e+09 \n", + "68422 1.504850e+12 4.657391e+11 7.016958e+11 7.293628e+09 \n", + "\n", + " operating_costs operating_profit cash \\\n", + "1945 6.884921e+09 1.958371e+09 3.249035e+09 \n", + "3432 1.144126e+10 3.426907e+09 1.074370e+10 \n", + "5449 2.060734e+10 7.652897e+09 1.704650e+10 \n", + "7921 2.500527e+10 6.364790e+09 1.997829e+10 \n", + "10605 3.451472e+10 8.685083e+09 2.300192e+10 \n", + "13934 3.007350e+10 1.189489e+10 3.781693e+10 \n", + "17289 4.322816e+10 1.576322e+10 3.423951e+10 \n", + "20376 6.542161e+10 2.101304e+10 5.229154e+10 \n", + "25215 9.279765e+10 2.426134e+10 4.436541e+10 \n", + "29387 1.025571e+11 2.497936e+10 6.271525e+10 \n", + "33753 1.381506e+11 3.312278e+10 5.318038e+10 \n", + "37463 1.697424e+11 3.902378e+10 8.703212e+10 \n", + "42678 1.600799e+11 5.081292e+10 1.741210e+11 \n", + "47756 1.861042e+11 6.749861e+10 1.884174e+11 \n", + "52944 2.345503e+11 7.661314e+10 1.661946e+11 \n", + "57414 2.965407e+11 7.995864e+10 1.952307e+11 \n", + "59180 3.539771e+11 5.253100e+10 1.493524e+11 \n", + "62929 4.053193e+11 5.200694e+10 1.372076e+11 \n", + "68422 3.947839e+11 2.925170e+10 9.981376e+10 \n", + "\n", + " operating_net_cash_flow score prev_total_stockholder_interest \\\n", + "1945 8.434391e+08 0.0 NaN \n", + "3432 -3.024121e+09 0.0 8.581223e+09 \n", + "5449 -1.043772e+10 0.0 1.745350e+10 \n", + "7921 -3.415183e+07 0.0 3.391952e+10 \n", + "10605 9.253351e+09 0.0 3.881855e+10 \n", + "13934 2.237255e+09 250.0 4.540851e+10 \n", + "17289 3.389425e+09 0.0 5.458620e+10 \n", + "20376 3.725958e+09 0.0 6.783254e+10 \n", + "25215 1.923869e+09 300.0 8.213819e+10 \n", + "29387 4.172482e+10 0.0 1.054394e+11 \n", + "33753 1.604602e+10 0.0 1.158936e+11 \n", + "37463 3.956613e+10 400.0 1.363096e+11 \n", + "42678 8.232283e+10 250.0 1.616766e+11 \n", + "47756 3.361818e+10 0.0 1.866739e+11 \n", + "52944 4.568681e+10 400.0 2.356207e+11 \n", + "57414 5.318802e+10 0.0 2.705791e+11 \n", + "59180 4.113161e+09 250.0 3.498445e+11 \n", + "62929 2.750449e+09 0.0 3.927728e+11 \n", + "68422 3.912324e+09 300.0 4.049915e+11 \n", + "\n", + " roe average_roe \n", + "1945 NaN NaN \n", + "3432 0.186136 NaN \n", + "5449 0.207015 NaN \n", + "7921 0.127577 NaN \n", + "10605 0.152683 NaN \n", + "13934 0.176802 0.170043 \n", + "17289 0.189507 0.170717 \n", + "20376 0.208875 0.171089 \n", + "25215 0.195093 0.184592 \n", + "29387 0.174285 0.188912 \n", + "33753 0.205782 0.194708 \n", + "37463 0.190279 0.194863 \n", + "42678 0.213626 0.195813 \n", + "47756 0.233355 0.203465 \n", + "52944 0.217825 0.212174 \n", + "57414 0.191154 0.209248 \n", + "59180 0.102528 0.191698 \n", + "62929 0.094140 0.167801 \n", + "68422 0.050637 0.131257 " ], "text/html": [ "
\n", @@ -132,223 +373,487 @@ " \n", " \n", " \n", + " code\n", " year\n", + " total_stockholder_interest\n", " net_income\n", + " total_assets\n", + " total_revenue\n", + " inventories\n", + " accounts_receivable\n", + " operating_costs\n", + " operating_profit\n", + " cash\n", + " operating_net_cash_flow\n", + " score\n", + " prev_total_stockholder_interest\n", + " roe\n", + " average_roe\n", " \n", " \n", " \n", " \n", - " 1808\n", + " 1945\n", + " 000002.SZ\n", " 2005\n", - " 3.467468e+06\n", + " 8.581223e+09\n", + " 1.433426e+09\n", + " 2.199239e+10\n", + " 1.055885e+10\n", + " 1.484948e+10\n", + " 3.773077e+08\n", + " 6.884921e+09\n", + " 1.958371e+09\n", + " 3.249035e+09\n", + " 8.434391e+08\n", + " 0.0\n", + " NaN\n", + " NaN\n", + " NaN\n", " \n", " \n", - " 3228\n", + " 3432\n", + " 000002.SZ\n", " 2006\n", - " 1.212677e+06\n", + " 1.745350e+10\n", + " 2.422997e+09\n", + " 4.991984e+10\n", + " 1.791833e+10\n", + " 3.416711e+10\n", + " 3.646097e+08\n", + " 1.144126e+10\n", + " 3.426907e+09\n", + " 1.074370e+10\n", + " -3.024121e+09\n", + " 0.0\n", + " 8.581223e+09\n", + " 0.186136\n", + " NaN\n", " \n", " \n", - " 4784\n", + " 5449\n", + " 000002.SZ\n", " 2007\n", - " 1.015147e+07\n", + " 3.391952e+10\n", + " 5.317501e+09\n", + " 1.000945e+11\n", + " 3.552661e+10\n", + " 6.647288e+10\n", + " 8.648830e+08\n", + " 2.060734e+10\n", + " 7.652897e+09\n", + " 1.704650e+10\n", + " -1.043772e+10\n", + " 0.0\n", + " 1.745350e+10\n", + " 0.207015\n", + " NaN\n", " \n", " \n", - " 8425\n", + " 7921\n", + " 000002.SZ\n", " 2008\n", - " 1.466373e+07\n", + " 3.881855e+10\n", + " 4.639869e+09\n", + " 1.192366e+11\n", + " 4.099178e+10\n", + " 8.589870e+10\n", + " 9.227748e+08\n", + " 2.500527e+10\n", + " 6.364790e+09\n", + " 1.997829e+10\n", + " -3.415183e+07\n", + " 0.0\n", + " 3.391952e+10\n", + " 0.127577\n", + " NaN\n", " \n", " \n", - " 9811\n", + " 10605\n", + " 000002.SZ\n", " 2009\n", - " 2.172666e+07\n", + " 4.540851e+10\n", + " 6.430008e+09\n", + " 1.376086e+11\n", + " 4.888101e+10\n", + " 9.008529e+10\n", + " 7.131919e+08\n", + " 3.451472e+10\n", + " 8.685083e+09\n", + " 2.300192e+10\n", + " 9.253351e+09\n", + " 0.0\n", + " 3.881855e+10\n", + " 0.152683\n", + " NaN\n", " \n", " \n", - " 12426\n", + " 13934\n", + " 000002.SZ\n", " 2010\n", - " 4.982651e+07\n", + " 5.458620e+10\n", + " 8.839611e+09\n", + " 2.156376e+11\n", + " 5.071385e+10\n", + " 1.333335e+11\n", + " 1.594025e+09\n", + " 3.007350e+10\n", + " 1.189489e+10\n", + " 3.781693e+10\n", + " 2.237255e+09\n", + " 250.0\n", + " 4.540851e+10\n", + " 0.176802\n", + " 0.170043\n", " \n", " \n", - " 17669\n", + " 17289\n", + " 000002.SZ\n", " 2011\n", - " 7.169039e+07\n", + " 6.783254e+10\n", + " 1.159961e+10\n", + " 2.962084e+11\n", + " 7.178275e+10\n", + " 2.083355e+11\n", + " 1.514814e+09\n", + " 4.322816e+10\n", + " 1.576322e+10\n", + " 3.423951e+10\n", + " 3.389425e+09\n", + " 0.0\n", + " 5.458620e+10\n", + " 0.189507\n", + " 0.170717\n", " \n", " \n", - " 19876\n", + " 20376\n", + " 000002.SZ\n", " 2012\n", - " 9.554584e+07\n", + " 8.213819e+10\n", + " 1.566259e+10\n", + " 3.788016e+11\n", + " 1.031162e+11\n", + " 2.551641e+11\n", + " 1.886549e+09\n", + " 6.542161e+10\n", + " 2.101304e+10\n", + " 5.229154e+10\n", + " 3.725958e+09\n", + " 0.0\n", + " 6.783254e+10\n", + " 0.208875\n", + " 0.171089\n", " \n", " \n", - " 25082\n", + " 25215\n", + " 000002.SZ\n", " 2013\n", - " 1.024329e+08\n", + " 1.054394e+11\n", + " 1.829755e+10\n", + " 4.792053e+11\n", + " 1.354188e+11\n", + " 3.311332e+11\n", + " 3.078970e+09\n", + " 9.279765e+10\n", + " 2.426134e+10\n", + " 4.436541e+10\n", + " 1.923869e+09\n", + " 300.0\n", + " 8.213819e+10\n", + " 0.195093\n", + " 0.184592\n", " \n", " \n", - " 28480\n", + " 29387\n", + " 000002.SZ\n", " 2014\n", - " 1.138340e+08\n", + " 1.158936e+11\n", + " 1.928752e+10\n", + " 5.084088e+11\n", + " 1.463880e+11\n", + " 3.177264e+11\n", + " 1.894072e+09\n", + " 1.025571e+11\n", + " 2.497936e+10\n", + " 6.271525e+10\n", + " 4.172482e+10\n", + " 0.0\n", + " 1.054394e+11\n", + " 0.174285\n", + " 0.188912\n", " \n", " \n", - " 33116\n", + " 33753\n", + " 000002.SZ\n", " 2015\n", - " 1.936414e+08\n", + " 1.363096e+11\n", + " 2.594944e+10\n", + " 6.112956e+11\n", + " 1.955491e+11\n", + " 3.681219e+11\n", + " 2.510653e+09\n", + " 1.381506e+11\n", + " 3.312278e+10\n", + " 5.318038e+10\n", + " 1.604602e+10\n", + " 0.0\n", + " 1.158936e+11\n", + " 0.205782\n", + " 0.194708\n", " \n", " \n", - " 37622\n", + " 37463\n", + " 000002.SZ\n", " 2016\n", - " 1.328421e+08\n", + " 1.616766e+11\n", + " 2.835026e+10\n", + " 8.306742e+11\n", + " 2.404772e+11\n", + " 4.673613e+11\n", + " 2.075257e+09\n", + " 1.697424e+11\n", + " 3.902378e+10\n", + " 8.703212e+10\n", + " 3.956613e+10\n", + " 400.0\n", + " 1.363096e+11\n", + " 0.190279\n", + " 0.194863\n", " \n", " \n", - " 41906\n", + " 42678\n", + " 000002.SZ\n", " 2017\n", - " 2.266875e+08\n", + " 1.866739e+11\n", + " 3.720839e+10\n", + " 1.165347e+12\n", + " 2.428971e+11\n", + " 5.980877e+11\n", + " 1.432734e+09\n", + " 1.600799e+11\n", + " 5.081292e+10\n", + " 1.741210e+11\n", + " 8.232283e+10\n", + " 250.0\n", + " 1.616766e+11\n", + " 0.213626\n", + " 0.195813\n", " \n", " \n", - " 47465\n", + " 47756\n", + " 000002.SZ\n", " 2018\n", - " 3.592014e+08\n", + " 2.356207e+11\n", + " 4.927229e+10\n", + " 1.528579e+12\n", + " 2.976793e+11\n", + " 7.503026e+11\n", + " 1.586181e+09\n", + " 1.861042e+11\n", + " 6.749861e+10\n", + " 1.884174e+11\n", + " 3.361818e+10\n", + " 0.0\n", + " 1.866739e+11\n", + " 0.233355\n", + " 0.203465\n", " \n", " \n", - " 51158\n", + " 52944\n", + " 000002.SZ\n", " 2019\n", - " 5.077704e+08\n", + " 2.705791e+11\n", + " 5.513161e+10\n", + " 1.729929e+12\n", + " 3.678939e+11\n", + " 8.970190e+11\n", + " 1.988076e+09\n", + " 2.345503e+11\n", + " 7.661314e+10\n", + " 1.661946e+11\n", + " 4.568681e+10\n", + " 400.0\n", + " 2.356207e+11\n", + " 0.217825\n", + " 0.212174\n", " \n", " \n", - " 54614\n", + " 57414\n", + " 000002.SZ\n", " 2020\n", - " 5.449616e+08\n", + " 3.498445e+11\n", + " 5.929812e+10\n", + " 1.869177e+12\n", + " 4.191117e+11\n", + " 1.002063e+12\n", + " 2.992423e+09\n", + " 2.965407e+11\n", + " 7.995864e+10\n", + " 1.952307e+11\n", + " 5.318802e+10\n", + " 0.0\n", + " 2.705791e+11\n", + " 0.191154\n", + " 0.209248\n", " \n", " \n", - " 61814\n", + " 59180\n", + " 000002.SZ\n", " 2021\n", - " 7.861501e+08\n", + " 3.927728e+11\n", + " 3.806953e+10\n", + " 1.938638e+12\n", + " 4.527978e+11\n", + " 1.075617e+12\n", + " 4.743597e+09\n", + " 3.539771e+11\n", + " 5.253100e+10\n", + " 1.493524e+11\n", + " 4.113161e+09\n", + " 250.0\n", + " 3.498445e+11\n", + " 0.102528\n", + " 0.191698\n", " \n", " \n", - " 66356\n", + " 62929\n", + " 000002.SZ\n", " 2022\n", - " 6.156781e+08\n", + " 4.049915e+11\n", + " 3.755091e+10\n", + " 1.757124e+12\n", + " 5.038384e+11\n", + " 9.070569e+11\n", + " 7.504692e+09\n", + " 4.053193e+11\n", + " 5.200694e+10\n", + " 1.372076e+11\n", + " 2.750449e+09\n", + " 0.0\n", + " 3.927728e+11\n", + " 0.094140\n", + " 0.167801\n", " \n", " \n", - " 71478\n", + " 68422\n", + " 000002.SZ\n", " 2023\n", - " 5.784234e+08\n", + " 4.029335e+11\n", + " 2.045556e+10\n", + " 1.504850e+12\n", + " 4.657391e+11\n", + " 7.016958e+11\n", + " 7.293628e+09\n", + " 3.947839e+11\n", + " 2.925170e+10\n", + " 9.981376e+10\n", + " 3.912324e+09\n", + " 300.0\n", + " 4.049915e+11\n", + " 0.050637\n", + " 0.131257\n", " \n", " \n", "\n", "
" ] }, - "execution_count": 4, + "execution_count": 150, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 4 - }, - { - "metadata": {}, - "cell_type": "markdown", - "source": "过滤上市时间大于5年的企业,由于计算很多数值需要与前一年比,所以实际上需要的是上市时间至少6年的企业", - "id": "3052b8c4442f3643" + "execution_count": 150 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-01-14T14:22:57.782337Z", - "start_time": "2025-01-14T14:22:57.774714Z" + "end_time": "2025-01-17T10:19:25.031141Z", + "start_time": "2025-01-17T10:19:25.020325Z" } }, "cell_type": "code", "source": [ - "from datetime import datetime\n", - "\n", - "select_df = stock_df[[\"ts_code\", \"list_date\"]]\n", - "select_df.loc[:, \"list_date\"] = pd.to_datetime(select_df[\"list_date\"], format=\"%Y%m%d\").dt.year\n", - "select_df = select_df[select_df[\"list_date\"] < datetime.now().year - 6]\n", - "codes = select_df[\"ts_code\"].tolist()\n", - "codes = map(lambda x: [x, stock_df[stock_df[\"ts_code\"] == x][\"name\"].values[0], 0], codes)" + "df = pd.DataFrame([[1, 2, 0], [1, 3, 0], [1, -4, 0]], columns=[\"code\", \"average\", \"score\"])\n", + "df[\"condition\"] = df.groupby(\"code\")[\"average\"].transform(\n", + " lambda x: x.rolling(window=2).apply(lambda y: (y < 0).any(), raw=False)\n", + ") > 0\n", + "df[\"score\"].mask(\n", + " df.groupby(\"code\")[\"average\"].transform(\n", + " lambda x: x.rolling(window=2).apply(lambda y: (y < 0).any(), raw=False)\n", + " ) > 0,\n", + " 0\n", + ")\n", + "df" ], - "id": "4293bd93ea8f9ed", - "outputs": [], - "execution_count": 5 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-01-14T14:22:59.917536Z", - "start_time": "2025-01-14T14:22:59.914056Z" + "id": "1a6a739df5b6f3a9", + "outputs": [ + { + "data": { + "text/plain": [ + " code average score condition\n", + "0 1 2 0 False\n", + "1 1 3 0 False\n", + "2 1 -4 0 True" + ], + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
codeaveragescorecondition
0120False
1130False
21-40True
\n", + "
" + ] + }, + "execution_count": 165, + "metadata": {}, + "output_type": "execute_result" } - }, - "cell_type": "code", - "source": [ - "def add_score(code_list, score):\n", - " code_list[2] = code_list[2] + score\n", - " code_list.append(score)\n", - " return code_list" ], - "id": "fa22f7c620511f0a", - "outputs": [], - "execution_count": 6 - }, - { - "metadata": {}, - "cell_type": "markdown", - "source": [ - "ROE\n", - "\n", - "股东权益报酬率(%) RoE\t股东权益报酬率RoE 是判断股票是否具备”长期上涨潜力“最重要的指标之一, RoE 如果既稳定又优秀,那这家公司的其他指标也基本不会差。\n", - "RoE均值 >= 35\t550分\n", - "35 > RoE均值 >= 30\t500分\n", - "30 > RoE均值 >= 25\t450分\n", - "25 > RoE均值 >= 20\t400分\n", - "20 > RoE均值 >= 15\t300分\n", - "15 > RoE均值 >= 10\t250分\n", - "10 > RoE均值 或 0 >= 其中一年\t0分" - ], - "id": "336aa970ee46709d" - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-01-14T14:23:11.032339Z", - "start_time": "2025-01-14T14:23:02.481495Z" - } - }, - "cell_type": "code", - "source": [ - "def cal_roe(df):\n", - " df[\"prev_total_stockholder_interest\"] = df[\"total_stockholder_interest\"].shift(1)\n", - " df[\"roe\"] = df[\"net_income\"] / ((df[\"prev_total_stockholder_interest\"] + df[\"total_stockholder_interest\"]) / 2)\n", - " df.drop(columns=[\"prev_total_stockholder_interest\"], inplace=True)\n", - "\n", - "\n", - "def score_by_roe(code):\n", - " temp_df = finance_df[finance_df[\"code\"] == code].copy()\n", - " cal_roe(temp_df)\n", - "\n", - " if any(roe < 0 for roe in temp_df[\"roe\"]):\n", - " return 0\n", - "\n", - " average_roe = temp_df.nlargest(5, \"year\")[\"roe\"].mean() * 100\n", - "\n", - " if average_roe >= 35:\n", - " return 550\n", - " elif average_roe >= 30:\n", - " return 500\n", - " elif average_roe >= 25:\n", - " return 450\n", - " elif average_roe >= 15:\n", - " return 300\n", - " elif average_roe >= 10:\n", - " return 250\n", - " else:\n", - " return 0\n", - "\n", - "\n", - "codes = list(map(lambda x: add_score(x, score_by_roe(x[0])), codes))" - ], - "id": "f050d33c4a0cd720", - "outputs": [], - "execution_count": 7 + "execution_count": 165 }, { "metadata": {}, @@ -763,11 +1268,13 @@ }, "cell_type": "code", "source": [ - "df = pd.DataFrame(codes,\n", - " columns=[\"code\", \"name\", \"score\", \"roe_score\", \"roa_score\", \"net_income\", \"assets_turnover_and_cash\",\n", - " \"collection_cash_period_and_sales_period\", \"gross_profit_ratio_volatility\",\n", - " \"operating_safety_margin\", \"net_income_ascending\",\n", - " \"operating_net_cash_flow_ascending\"])\n", + "df = pd.DataFrame(\n", + " codes,\n", + " columns=[\"code\", \"name\", \"score\", \"roe_score\", \"roa_score\", \"net_income\", \"assets_turnover_and_cash\",\n", + " \"collection_cash_period_and_sales_period\", \"gross_profit_ratio_volatility\",\n", + " \"operating_safety_margin\", \"net_income_ascending\",\n", + " \"operating_net_cash_flow_ascending\"]\n", + ")\n", "df.sort_values(by=\"score\", ascending=False, inplace=True)\n", "df" ], @@ -1444,8 +1951,10 @@ ], "execution_count": 206, "source": [ - "temp_df = ts_pro.fina_indicator(ts_code=\"600763.SH\", start_date=\"20140101\", end_date=\"20241231\",\n", - " fields=\"ts_code,end_date,roe,roa\")\n", + "temp_df = ts_pro.fina_indicator(\n", + " ts_code=\"600763.SH\", start_date=\"20140101\", end_date=\"20241231\",\n", + " fields=\"ts_code,end_date,roe,roa\"\n", + ")\n", "temp_df = temp_df[temp_df[\"end_date\"].str.endswith(\"1231\")]\n", "# temp_df[\"end_date\"] = temp_df[\"end_date\"].str[:4]\n", "# temp_df = temp_df.drop_duplicates(subset=[\"end_date\"], keep=\"last\")\n",