1
0
Files
finance/财报筛选/金字塔选股.ipynb

1388 lines
46 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2025-01-13T03:10:01.926908Z",
"start_time": "2025-01-13T03:10:01.923521Z"
}
},
"source": [
"import pandas as pd\n",
"import tushare as ts\n",
"\n",
"ts_pro = ts.pro_api(token=\"64ebff4fa679167600b905ee45dd88e76f3963c0ff39157f3f085f0e\")"
],
"outputs": [],
"execution_count": 182
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:10:03.759538Z",
"start_time": "2025-01-13T03:10:02.003985Z"
}
},
"cell_type": "code",
"source": [
"# 加载股票信息\n",
"stock_df = pd.read_csv(\"../材料/股票基础信息.csv\")\n",
"# 加载财报信息\n",
"source_finance_df = pd.read_csv(\"../temp/finance.csv\")\n",
"finance_df = pd.DataFrame()\n",
"finance_df[[\n",
" \"code\",\n",
" # 年份\n",
" \"year\",\n",
" # 股东权益合计(含少数股东权益)\n",
" \"total_stockholder_interest\",\n",
" # 净利润\n",
" \"net_income\",\n",
" # 总资产\n",
" \"total_assets\",\n",
" # 营业总收入\n",
" \"total_revenue\",\n",
" # 存货\n",
" \"inventories\",\n",
" # 应收账款\n",
" \"accounts_receivable\",\n",
" # 营业成本\n",
" \"operating_costs\",\n",
" # 营业利润\n",
" \"operating_profit\",\n",
" # 现金与现金等价物\n",
" \"cash\",\n",
" # 营业活动现金流量净值\n",
" \"operating_net_cash_flow\",\n",
"]] = source_finance_df[[\n",
" \"ts_code\",\n",
" \"end_date\",\n",
" \"total_hldr_eqy_inc_min_int\",\n",
" \"n_income\",\n",
" \"total_assets\",\n",
" \"total_revenue\",\n",
" \"inventories\",\n",
" \"accounts_receiv\",\n",
" \"oper_cost\",\n",
" \"operate_profit\",\n",
" \"money_cap\",\n",
" \"n_cashflow_act\",\n",
"]]"
],
"id": "68b2debc14502fd5",
"outputs": [],
"execution_count": 183
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:10:03.773392Z",
"start_time": "2025-01-13T03:10:03.761918Z"
}
},
"cell_type": "code",
"source": "finance_df[finance_df[\"code\"] == \"600763.SH\"][[\"year\", \"net_income\"]]",
"id": "f0e8942b3403348c",
"outputs": [
{
"data": {
"text/plain": [
" year net_income\n",
"2979 2014 1.138340e+08\n",
"7615 2015 1.936414e+08\n",
"12121 2016 1.328421e+08\n",
"16405 2017 2.266875e+08\n",
"21964 2018 3.592014e+08\n",
"25657 2019 5.077704e+08\n",
"29113 2020 5.449616e+08\n",
"36313 2021 7.861501e+08\n",
"40855 2022 6.156781e+08\n",
"45977 2023 5.784234e+08"
],
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>year</th>\n",
" <th>net_income</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2979</th>\n",
" <td>2014</td>\n",
" <td>1.138340e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7615</th>\n",
" <td>2015</td>\n",
" <td>1.936414e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12121</th>\n",
" <td>2016</td>\n",
" <td>1.328421e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16405</th>\n",
" <td>2017</td>\n",
" <td>2.266875e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21964</th>\n",
" <td>2018</td>\n",
" <td>3.592014e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25657</th>\n",
" <td>2019</td>\n",
" <td>5.077704e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>29113</th>\n",
" <td>2020</td>\n",
" <td>5.449616e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>36313</th>\n",
" <td>2021</td>\n",
" <td>7.861501e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>40855</th>\n",
" <td>2022</td>\n",
" <td>6.156781e+08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>45977</th>\n",
" <td>2023</td>\n",
" <td>5.784234e+08</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
]
},
"execution_count": 184,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 184
},
{
"metadata": {},
"cell_type": "markdown",
"source": "过滤上市时间大于5年的企业由于计算很多数值需要与前一年比所以实际上需要的是上市时间至少6年的企业",
"id": "3052b8c4442f3643"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:10:03.784454Z",
"start_time": "2025-01-13T03:10:03.774686Z"
}
},
"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)"
],
"id": "4293bd93ea8f9ed",
"outputs": [],
"execution_count": 185
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:10:03.789557Z",
"start_time": "2025-01-13T03:10:03.786826Z"
}
},
"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": 186
},
{
"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-13T03:10:23.578470Z",
"start_time": "2025-01-13T03:10:03.791003Z"
}
},
"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": 187
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"总资产报酬率(%) RoA\t总资产报酬率RoA 能真实反映出一家公司的获利能力, 相当于一家公司的 「投资年化率」,当然越高越好。\n",
"RoA均值 >= 15\t100分\n",
"15 > RoA均值 >= 11\t80分\n",
"11 > RoA均值 >= 7\t50分\n",
"7 > RoA均值\t0分"
],
"id": "6c2e1c602898e342"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:10:42.654737Z",
"start_time": "2025-01-13T03:10:23.580200Z"
}
},
"cell_type": "code",
"source": [
"def cal_roa(df):\n",
" df[\"prev_total_assets\"] = df[\"total_assets\"].shift(1)\n",
" df[\"roa\"] = df[\"net_income\"] / ((df[\"prev_total_assets\"] + df[\"total_assets\"]) / 2)\n",
" df.drop(columns=[\"prev_total_assets\"], inplace=True)\n",
"\n",
"\n",
"def score_by_roa(code):\n",
" temp_df = finance_df[finance_df[\"code\"] == code].copy()\n",
" cal_roa(temp_df)\n",
"\n",
" average_roa = temp_df.nlargest(5, \"year\")[\"roa\"].mean() * 100\n",
"\n",
" if average_roa >= 15:\n",
" return 100\n",
" elif average_roa >= 11:\n",
" return 80\n",
" elif average_roa >= 7:\n",
" return 50\n",
" else:\n",
" return 0\n",
"\n",
"\n",
"codes = list(map(lambda x: add_score(x, score_by_roa(x[0])), codes))"
],
"id": "25d1d5c0f7de460c",
"outputs": [],
"execution_count": 188
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"税后净利 规模(百万)\t获利规模大那股价的”长期上涨潜力“也就会越稳健、越确定。选择时大公司的优先级要高于小公司。\n",
"税后净利均值 >= 10000\t150分\n",
"10000 > 税后净利均值 >= 1000\t100分\n",
"1000 > 税后净利均值\t0分"
],
"id": "dbcb1d6b23582f3e"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:10:58.085277Z",
"start_time": "2025-01-13T03:10:42.657424Z"
}
},
"cell_type": "code",
"source": [
"def score_by_net_income(code):\n",
" temp_df = finance_df[finance_df[\"code\"] == code].copy()\n",
" average_net_income = temp_df.nlargest(5, \"year\")[\"net_income\"].mean()\n",
"\n",
" if average_net_income >= 10000 * 10000000:\n",
" return 150\n",
" elif average_net_income >= 1000 * 10000000:\n",
" return 100\n",
" else:\n",
" return 0\n",
"\n",
"\n",
"codes = list(map(lambda x: add_score(x, score_by_net_income(x[0])), codes))"
],
"id": "2f19c7fbb0afaa49",
"outputs": [],
"execution_count": 189
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"现金状况 分析\t总资产周转率 与 现金与约当现金占总资产的关系,可以看出公司的现金状况是否健康。\n",
"规则①:总资产周转率 > 0.8 且 现金与约当现金占总资产(%) >= 10\t50分\n",
"规则②:总资产周转率 < 0.8 且 现金与约当现金占总资产(%) >= 20\t50分\n",
"不符合 规则① 或 规则②\t0分"
],
"id": "2c6c934a376eef0d"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:11:20.221255Z",
"start_time": "2025-01-13T03:10:58.087417Z"
}
},
"cell_type": "code",
"source": [
"def cal_total_assets_turnover_ratio(df):\n",
" df[\"prev_total_assets\"] = df[\"total_assets\"].shift(1)\n",
" df[\"total_assets_turnover_ratio\"] = df[\"total_revenue\"] / ((df[\"prev_total_assets\"] + df[\"total_assets\"]) / 2)\n",
" df.drop(columns=[\"prev_total_assets\"], inplace=True)\n",
"\n",
"\n",
"def cal_cash_ratio(df):\n",
" df[\"cash_ratio\"] = df[\"cash\"] / df[\"total_assets\"]\n",
"\n",
"\n",
"def score_by_assets_turnover_and_cash(code):\n",
" temp_df = finance_df[finance_df[\"code\"] == code].copy()\n",
"\n",
" # 总资产周转率\n",
" cal_total_assets_turnover_ratio(temp_df)\n",
" total_assets_turnover_ratio = \\\n",
" temp_df[temp_df[\"year\"] == temp_df[\"year\"].max()][\"total_assets_turnover_ratio\"].values[0] * 100\n",
"\n",
" # 现金比率\n",
" cal_cash_ratio(temp_df)\n",
" cash_ratio = temp_df[temp_df[\"year\"] == temp_df[\"year\"].max()][\"cash_ratio\"].values[0] * 100\n",
"\n",
" if total_assets_turnover_ratio > 80 and cash_ratio >= 10:\n",
" return 50\n",
" elif total_assets_turnover_ratio < 80 and cash_ratio >= 20:\n",
" return 50\n",
" else:\n",
" return 0\n",
"\n",
"\n",
"codes = list(map(lambda x: add_score(x, score_by_assets_turnover_and_cash(x[0])), codes))"
],
"id": "31be2cb671b7adde",
"outputs": [],
"execution_count": 190
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"收现日数(日)\t平均收现日数越短说明公司的经营能力越强。\n",
"30 >= 平均收现日数\t20分\n",
"平均收现日数 > 30\t0分\n",
"\n",
"销货日数(日)\t平均销货日数越短说明公司商品越畅销。\n",
"30 >= 平均销货日数\t20分\n",
"平均销货日数 > 30\t0分\n",
"\n",
"收现日数+销货日数(日)\t生意周期 是否足够优秀,越短说明每年能做的生意趟数越多,经营能力就越强。\n",
"40 >= 收现日数+销货日数\t20分\n",
"60 >= 收现日数+销货日数 > 40\t10分\n",
"平均销货日数 > 60\t0分"
],
"id": "41ac526b0296b44b"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:11:39.666059Z",
"start_time": "2025-01-13T03:11:20.222910Z"
}
},
"cell_type": "code",
"source": [
"# 收现日数\n",
"def cal_collection_cash_period(df):\n",
" df[\"collection_cash_period\"] = 360 / (df[\"total_revenue\"] / df[\"accounts_receivable\"])\n",
"\n",
"\n",
"# 销货日数\n",
"def cal_sales_period(df):\n",
" df[\"sales_period\"] = 360 / (df[\"operating_costs\"] / df[\"inventories\"])\n",
"\n",
"\n",
"def score_by_collection_cash_period_and_sales_period(code):\n",
" temp_df = finance_df[finance_df[\"code\"] == code].copy()\n",
"\n",
" cal_collection_cash_period(temp_df)\n",
" collection_cash_period = temp_df[temp_df[\"year\"] == temp_df[\"year\"].max()][\"collection_cash_period\"].values[0]\n",
"\n",
" cal_sales_period(temp_df)\n",
" sales_period = temp_df[temp_df[\"year\"] == temp_df[\"year\"].max()][\"sales_period\"].values[0]\n",
"\n",
" score = 0\n",
" if collection_cash_period < 30:\n",
" score += 20\n",
"\n",
" if sales_period < 30:\n",
" score += 20\n",
"\n",
" if (collection_cash_period + sales_period) < 40:\n",
" score += 20\n",
" elif (collection_cash_period + sales_period) < 60:\n",
" score += 10\n",
"\n",
" return score\n",
"\n",
"\n",
"codes = list(map(lambda x: add_score(x, score_by_collection_cash_period_and_sales_period(x[0])), codes))"
],
"id": "ab6ff79c2650db7c",
"outputs": [],
"execution_count": 191
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"毛利率(%)\t毛利率 是否保持平稳,不大起大落。\n",
"30% >= 毛利率平均波动幅度\t50分\n",
"毛利率平均波动幅度 > 30%\t0分"
],
"id": "a53bfdb799df8f48"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:11:57.137395Z",
"start_time": "2025-01-13T03:11:39.669792Z"
}
},
"cell_type": "code",
"source": [
"# 毛利率\n",
"def cal_gross_profit_ratio(df):\n",
" df[\"gross_profit_ratio\"] = (df[\"total_revenue\"] - df[\"operating_costs\"]) / df[\"total_revenue\"]\n",
"\n",
"\n",
"def score_by_gross_profit_ratio_volatility(code):\n",
" temp_df = finance_df[finance_df[\"code\"] == code].copy()\n",
" cal_gross_profit_ratio(temp_df)\n",
" gross_profit_ratio_std = temp_df.nlargest(5, \"year\")[\"gross_profit_ratio\"].std() * 100\n",
"\n",
" if gross_profit_ratio_std < 30:\n",
" return 50\n",
" else:\n",
" return 0\n",
"\n",
"\n",
"codes = list(map(lambda x: add_score(x, score_by_gross_profit_ratio_volatility(x[0])), codes))"
],
"id": "8e58e0114851ebcb",
"outputs": [],
"execution_count": 192
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"经营安全边际率(%)\t经营安全边际率越高说明公司能够很好的控制成本把大部分利润转化成实际收益 获利能力很强,有利于抵御经济波动和价格竞争等影响。\n",
"经营安全边际率 >= 70\t50分\n",
"70 > 经营安全边际率 >= 50\t30分\n",
"50 > 经营安全边际率 >= 30\t10分\n",
"30 > 经营安全边际率\t0分"
],
"id": "6613ab8263d4afef"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:12:16.127651Z",
"start_time": "2025-01-13T03:11:57.138994Z"
}
},
"cell_type": "code",
"source": [
"# 经营安全边际\n",
"def cal_operating_safety_margin(df):\n",
" # 经营利润率\n",
" df[\"operating_profit_ratio\"] = df[\"operating_profit\"] / df[\"total_revenue\"]\n",
" df[\"operating_safety_margin\"] = df[\"operating_profit_ratio\"] / df[\"gross_profit_ratio\"]\n",
"\n",
"\n",
"def score_by_operating_safety_margin(code):\n",
" temp_df = finance_df[finance_df[\"code\"] == code].copy()\n",
" cal_gross_profit_ratio(temp_df)\n",
" cal_operating_safety_margin(temp_df)\n",
"\n",
" operating_safety_margin = temp_df[temp_df[\"year\"] == temp_df[\"year\"].max()][\"operating_safety_margin\"].values[0]\n",
"\n",
" if operating_safety_margin >= 70:\n",
" return 50\n",
" elif operating_safety_margin >= 50:\n",
" return 30\n",
" elif operating_safety_margin >= 30:\n",
" return 10\n",
" else:\n",
" return 0\n",
"\n",
"\n",
"codes = list(map(lambda x: add_score(x, score_by_operating_safety_margin(x[0])), codes))"
],
"id": "d07048f574e163ab",
"outputs": [],
"execution_count": 193
},
{
"metadata": {},
"cell_type": "markdown",
"source": [
"税后净利\n",
"年份由近(A)~远(E)\t公司赚的钱是否在逐年增长是股价能长期上涨的基础。\n",
"A年 > B年 (+) / A年 < B年 (-)\t+30分 / -30分\n",
"B年 > C年 (+) / B年 < C年 (-)\t+25分 / -25分\n",
"C年 > D年 (+) / C年 < D年 (-)\t+20分 / -20分\n",
"D年 > E年 (+) / D年 < E年 (-)\t+15分 / -15分"
],
"id": "2ee3712525455954"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:12:31.905761Z",
"start_time": "2025-01-13T03:12:16.129100Z"
}
},
"cell_type": "code",
"source": [
"def score_by_net_income_ascending(code):\n",
" temp_df = finance_df[finance_df[\"code\"] == code].copy()[[\"year\", \"net_income\"]]\n",
" temp_df.sort_values(by=\"year\", ascending=False, inplace=True)\n",
" temp_df.set_index(keys=\"year\", drop=True, inplace=True)\n",
"\n",
" score = 0\n",
" if temp_df.iloc[0].values[0] > temp_df.iloc[1].values[0]:\n",
" score += 30\n",
" else:\n",
" score -= 30\n",
"\n",
" if temp_df.iloc[1].values[0] > temp_df.iloc[2].values[0]:\n",
" score += 25\n",
" else:\n",
" score -= 25\n",
"\n",
" if temp_df.iloc[2].values[0] > temp_df.iloc[3].values[0]:\n",
" score += 20\n",
" else:\n",
" score -= 20\n",
"\n",
" if temp_df.iloc[3].values[0] > temp_df.iloc[4].values[0]:\n",
" score += 15\n",
" else:\n",
" score -= 15\n",
"\n",
" return score\n",
"\n",
"\n",
"codes = list(map(lambda x: add_score(x, score_by_net_income_ascending(x[0])), codes))"
],
"id": "4ab6a7b485dc9349",
"outputs": [],
"execution_count": 194
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:12:46.733835Z",
"start_time": "2025-01-13T03:12:31.907382Z"
}
},
"cell_type": "code",
"source": [
"def score_by_operating_net_cash_flow_ascending(code):\n",
" temp_df = finance_df[finance_df[\"code\"] == code].copy()[[\"year\", \"operating_net_cash_flow\"]]\n",
" temp_df.sort_values(by=\"year\", ascending=False, inplace=True)\n",
" temp_df.set_index(keys=\"year\", drop=True, inplace=True)\n",
"\n",
" score = 0\n",
" if temp_df.iloc[0].values[0] > temp_df.iloc[1].values[0]:\n",
" score += 30\n",
" else:\n",
" score -= 30\n",
"\n",
" if temp_df.iloc[1].values[0] > temp_df.iloc[2].values[0]:\n",
" score += 25\n",
" else:\n",
" score -= 25\n",
"\n",
" if temp_df.iloc[2].values[0] > temp_df.iloc[3].values[0]:\n",
" score += 20\n",
" else:\n",
" score -= 20\n",
"\n",
" if temp_df.iloc[3].values[0] > temp_df.iloc[4].values[0]:\n",
" score += 15\n",
" else:\n",
" score -= 15\n",
"\n",
" return score\n",
"\n",
"\n",
"codes = list(map(lambda x: add_score(x, score_by_operating_net_cash_flow_ascending(x[0])), codes))"
],
"id": "d6644089e803a79d",
"outputs": [],
"execution_count": 195
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:12:46.753922Z",
"start_time": "2025-01-13T03:12:46.734835Z"
}
},
"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.sort_values(by=\"score\", ascending=False, inplace=True)\n",
"df"
],
"id": "ef9e6259efc1c1d0",
"outputs": [
{
"data": {
"text/plain": [
" code name score roe_score roa_score net_income \\\n",
"2393 600519.SH 贵州茅台 950 500 100 100 \n",
"323 000858.SZ 五粮液 910 450 100 100 \n",
"144 000568.SZ 泸州老窖 850 450 100 0 \n",
"3238 603688.SH 石英股份 850 500 100 0 \n",
"2336 600438.SH 通威股份 840 450 80 100 \n",
"... ... ... ... ... ... ... \n",
"827 002425.SZ ST凯文 -100 0 0 0 \n",
"1485 300200.SZ 高盟新材 -100 0 0 0 \n",
"1619 300345.SZ 华民股份 -130 0 0 0 \n",
"1488 300204.SZ 舒泰神 -130 0 0 0 \n",
"1054 002672.SZ 东江环保 -130 0 0 0 \n",
"\n",
" assets_turnover_and_cash collection_cash_period_and_sales_period \\\n",
"2393 50 20 \n",
"323 50 20 \n",
"144 50 20 \n",
"3238 50 0 \n",
"2336 50 50 \n",
"... ... ... \n",
"827 0 0 \n",
"1485 0 0 \n",
"1619 0 0 \n",
"1488 0 0 \n",
"1054 0 0 \n",
"\n",
" gross_profit_ratio_volatility operating_safety_margin \\\n",
"2393 50 0 \n",
"323 50 0 \n",
"144 50 0 \n",
"3238 50 0 \n",
"2336 50 0 \n",
"... ... ... \n",
"827 50 0 \n",
"1485 50 0 \n",
"1619 50 0 \n",
"1488 50 0 \n",
"1054 50 0 \n",
"\n",
" net_income_ascending operating_net_cash_flow_ascending \n",
"2393 90 40 \n",
"323 90 50 \n",
"144 90 90 \n",
"3238 90 60 \n",
"2336 30 30 \n",
"... ... ... \n",
"827 -90 -60 \n",
"1485 -60 -90 \n",
"1619 -90 -90 \n",
"1488 -90 -90 \n",
"1054 -90 -90 \n",
"\n",
"[3384 rows x 12 columns]"
],
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>code</th>\n",
" <th>name</th>\n",
" <th>score</th>\n",
" <th>roe_score</th>\n",
" <th>roa_score</th>\n",
" <th>net_income</th>\n",
" <th>assets_turnover_and_cash</th>\n",
" <th>collection_cash_period_and_sales_period</th>\n",
" <th>gross_profit_ratio_volatility</th>\n",
" <th>operating_safety_margin</th>\n",
" <th>net_income_ascending</th>\n",
" <th>operating_net_cash_flow_ascending</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2393</th>\n",
" <td>600519.SH</td>\n",
" <td>贵州茅台</td>\n",
" <td>950</td>\n",
" <td>500</td>\n",
" <td>100</td>\n",
" <td>100</td>\n",
" <td>50</td>\n",
" <td>20</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>90</td>\n",
" <td>40</td>\n",
" </tr>\n",
" <tr>\n",
" <th>323</th>\n",
" <td>000858.SZ</td>\n",
" <td>五粮液</td>\n",
" <td>910</td>\n",
" <td>450</td>\n",
" <td>100</td>\n",
" <td>100</td>\n",
" <td>50</td>\n",
" <td>20</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>90</td>\n",
" <td>50</td>\n",
" </tr>\n",
" <tr>\n",
" <th>144</th>\n",
" <td>000568.SZ</td>\n",
" <td>泸州老窖</td>\n",
" <td>850</td>\n",
" <td>450</td>\n",
" <td>100</td>\n",
" <td>0</td>\n",
" <td>50</td>\n",
" <td>20</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>90</td>\n",
" <td>90</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3238</th>\n",
" <td>603688.SH</td>\n",
" <td>石英股份</td>\n",
" <td>850</td>\n",
" <td>500</td>\n",
" <td>100</td>\n",
" <td>0</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>90</td>\n",
" <td>60</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2336</th>\n",
" <td>600438.SH</td>\n",
" <td>通威股份</td>\n",
" <td>840</td>\n",
" <td>450</td>\n",
" <td>80</td>\n",
" <td>100</td>\n",
" <td>50</td>\n",
" <td>50</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>30</td>\n",
" <td>30</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>827</th>\n",
" <td>002425.SZ</td>\n",
" <td>ST凯文</td>\n",
" <td>-100</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>-90</td>\n",
" <td>-60</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1485</th>\n",
" <td>300200.SZ</td>\n",
" <td>高盟新材</td>\n",
" <td>-100</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>-60</td>\n",
" <td>-90</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1619</th>\n",
" <td>300345.SZ</td>\n",
" <td>华民股份</td>\n",
" <td>-130</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>-90</td>\n",
" <td>-90</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1488</th>\n",
" <td>300204.SZ</td>\n",
" <td>舒泰神</td>\n",
" <td>-130</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>-90</td>\n",
" <td>-90</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1054</th>\n",
" <td>002672.SZ</td>\n",
" <td>东江环保</td>\n",
" <td>-130</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>50</td>\n",
" <td>0</td>\n",
" <td>-90</td>\n",
" <td>-90</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>3384 rows × 12 columns</p>\n",
"</div>"
]
},
"execution_count": 196,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 196
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-13T03:17:08.882164Z",
"start_time": "2025-01-13T03:17:08.862820Z"
}
},
"cell_type": "code",
"source": [
"temp_df = finance_df[finance_df[\"code\"] == \"600763.SH\"]\n",
"cal_roe(temp_df)\n",
"cal_roa(temp_df)\n",
"temp_df[[\"year\", \"roe\", \"roa\"]]"
],
"id": "e6d973f4ff98ebef",
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/7h/w0cmp4zj6mn9br_6nyj310m40000gn/T/ipykernel_51974/3287426981.py:2: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df[\"prev_total_stockholder_interest\"] = df[\"total_stockholder_interest\"].shift(1)\n",
"/var/folders/7h/w0cmp4zj6mn9br_6nyj310m40000gn/T/ipykernel_51974/3287426981.py:3: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df[\"roe\"] = df[\"net_income\"] / ((df[\"prev_total_stockholder_interest\"] + df[\"total_stockholder_interest\"]) / 2)\n",
"/var/folders/7h/w0cmp4zj6mn9br_6nyj310m40000gn/T/ipykernel_51974/3287426981.py:4: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df.drop(columns=[\"prev_total_stockholder_interest\"], inplace=True)\n",
"/var/folders/7h/w0cmp4zj6mn9br_6nyj310m40000gn/T/ipykernel_51974/3105409447.py:2: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df[\"prev_total_assets\"] = df[\"total_assets\"].shift(1)\n",
"/var/folders/7h/w0cmp4zj6mn9br_6nyj310m40000gn/T/ipykernel_51974/3105409447.py:3: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df[\"roa\"] = df[\"net_income\"] / ((df[\"prev_total_assets\"] + df[\"total_assets\"]) / 2)\n",
"/var/folders/7h/w0cmp4zj6mn9br_6nyj310m40000gn/T/ipykernel_51974/3105409447.py:4: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" df.drop(columns=[\"prev_total_assets\"], inplace=True)\n"
]
},
{
"data": {
"text/plain": [
" year roe roa\n",
"2979 2014 NaN NaN\n",
"7615 2015 0.245119 0.182412\n",
"12121 2016 0.148244 0.101560\n",
"16405 2017 0.226923 0.147172\n",
"21964 2018 0.282053 0.182929\n",
"25657 2019 0.297166 0.211903\n",
"29113 2020 0.252412 0.190406\n",
"36313 2021 0.286244 0.196068\n",
"40855 2022 0.182081 0.121805\n",
"45977 2023 0.149449 0.103658"
],
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>year</th>\n",
" <th>roe</th>\n",
" <th>roa</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2979</th>\n",
" <td>2014</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7615</th>\n",
" <td>2015</td>\n",
" <td>0.245119</td>\n",
" <td>0.182412</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12121</th>\n",
" <td>2016</td>\n",
" <td>0.148244</td>\n",
" <td>0.101560</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16405</th>\n",
" <td>2017</td>\n",
" <td>0.226923</td>\n",
" <td>0.147172</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21964</th>\n",
" <td>2018</td>\n",
" <td>0.282053</td>\n",
" <td>0.182929</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25657</th>\n",
" <td>2019</td>\n",
" <td>0.297166</td>\n",
" <td>0.211903</td>\n",
" </tr>\n",
" <tr>\n",
" <th>29113</th>\n",
" <td>2020</td>\n",
" <td>0.252412</td>\n",
" <td>0.190406</td>\n",
" </tr>\n",
" <tr>\n",
" <th>36313</th>\n",
" <td>2021</td>\n",
" <td>0.286244</td>\n",
" <td>0.196068</td>\n",
" </tr>\n",
" <tr>\n",
" <th>40855</th>\n",
" <td>2022</td>\n",
" <td>0.182081</td>\n",
" <td>0.121805</td>\n",
" </tr>\n",
" <tr>\n",
" <th>45977</th>\n",
" <td>2023</td>\n",
" <td>0.149449</td>\n",
" <td>0.103658</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
]
},
"execution_count": 204,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 204
},
{
"metadata": {},
"cell_type": "code",
"outputs": [
{
"data": {
"text/plain": [
" ts_code end_date roe roa\n",
"4 600763.SH 20231231 14.0666 13.4808\n",
"10 600763.SH 20221231 17.7106 15.4075\n",
"11 600763.SH 20221231 17.7106 15.4075\n",
"18 600763.SH 20211231 27.9402 24.7367\n",
"19 600763.SH 20211231 27.9402 24.7367\n",
"26 600763.SH 20201231 25.0345 24.1350\n",
"27 600763.SH 20201231 25.0345 24.1350\n",
"34 600763.SH 20191231 29.8694 27.2723\n",
"35 600763.SH 20191231 29.8694 27.2723\n",
"42 600763.SH 20181231 28.7803 23.4500\n",
"43 600763.SH 20181231 28.7803 23.4500\n",
"50 600763.SH 20171231 23.9279 18.8885\n",
"51 600763.SH 20171231 23.9279 18.8885\n",
"58 600763.SH 20161231 16.6081 13.5829\n",
"59 600763.SH 20161231 16.6081 13.5829\n",
"66 600763.SH 20151231 26.5184 22.2861\n",
"67 600763.SH 20151231 26.5184 22.2861\n",
"74 600763.SH 20141231 19.1679 20.0538\n",
"75 600763.SH 20141231 19.1679 20.0538"
],
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>ts_code</th>\n",
" <th>end_date</th>\n",
" <th>roe</th>\n",
" <th>roa</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>600763.SH</td>\n",
" <td>20231231</td>\n",
" <td>14.0666</td>\n",
" <td>13.4808</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>600763.SH</td>\n",
" <td>20221231</td>\n",
" <td>17.7106</td>\n",
" <td>15.4075</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>600763.SH</td>\n",
" <td>20221231</td>\n",
" <td>17.7106</td>\n",
" <td>15.4075</td>\n",
" </tr>\n",
" <tr>\n",
" <th>18</th>\n",
" <td>600763.SH</td>\n",
" <td>20211231</td>\n",
" <td>27.9402</td>\n",
" <td>24.7367</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19</th>\n",
" <td>600763.SH</td>\n",
" <td>20211231</td>\n",
" <td>27.9402</td>\n",
" <td>24.7367</td>\n",
" </tr>\n",
" <tr>\n",
" <th>26</th>\n",
" <td>600763.SH</td>\n",
" <td>20201231</td>\n",
" <td>25.0345</td>\n",
" <td>24.1350</td>\n",
" </tr>\n",
" <tr>\n",
" <th>27</th>\n",
" <td>600763.SH</td>\n",
" <td>20201231</td>\n",
" <td>25.0345</td>\n",
" <td>24.1350</td>\n",
" </tr>\n",
" <tr>\n",
" <th>34</th>\n",
" <td>600763.SH</td>\n",
" <td>20191231</td>\n",
" <td>29.8694</td>\n",
" <td>27.2723</td>\n",
" </tr>\n",
" <tr>\n",
" <th>35</th>\n",
" <td>600763.SH</td>\n",
" <td>20191231</td>\n",
" <td>29.8694</td>\n",
" <td>27.2723</td>\n",
" </tr>\n",
" <tr>\n",
" <th>42</th>\n",
" <td>600763.SH</td>\n",
" <td>20181231</td>\n",
" <td>28.7803</td>\n",
" <td>23.4500</td>\n",
" </tr>\n",
" <tr>\n",
" <th>43</th>\n",
" <td>600763.SH</td>\n",
" <td>20181231</td>\n",
" <td>28.7803</td>\n",
" <td>23.4500</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50</th>\n",
" <td>600763.SH</td>\n",
" <td>20171231</td>\n",
" <td>23.9279</td>\n",
" <td>18.8885</td>\n",
" </tr>\n",
" <tr>\n",
" <th>51</th>\n",
" <td>600763.SH</td>\n",
" <td>20171231</td>\n",
" <td>23.9279</td>\n",
" <td>18.8885</td>\n",
" </tr>\n",
" <tr>\n",
" <th>58</th>\n",
" <td>600763.SH</td>\n",
" <td>20161231</td>\n",
" <td>16.6081</td>\n",
" <td>13.5829</td>\n",
" </tr>\n",
" <tr>\n",
" <th>59</th>\n",
" <td>600763.SH</td>\n",
" <td>20161231</td>\n",
" <td>16.6081</td>\n",
" <td>13.5829</td>\n",
" </tr>\n",
" <tr>\n",
" <th>66</th>\n",
" <td>600763.SH</td>\n",
" <td>20151231</td>\n",
" <td>26.5184</td>\n",
" <td>22.2861</td>\n",
" </tr>\n",
" <tr>\n",
" <th>67</th>\n",
" <td>600763.SH</td>\n",
" <td>20151231</td>\n",
" <td>26.5184</td>\n",
" <td>22.2861</td>\n",
" </tr>\n",
" <tr>\n",
" <th>74</th>\n",
" <td>600763.SH</td>\n",
" <td>20141231</td>\n",
" <td>19.1679</td>\n",
" <td>20.0538</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75</th>\n",
" <td>600763.SH</td>\n",
" <td>20141231</td>\n",
" <td>19.1679</td>\n",
" <td>20.0538</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
]
},
"execution_count": 206,
"metadata": {},
"output_type": "execute_result"
}
],
"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 = 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",
"temp_df"
],
"id": "b220252765677c76"
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}