本文介绍一个申万行业数据分析系统,它集成了数据获取、可视化、财务评价和回测分析等功能。通过这个项目,你将学会如何用Python构建一个专业级的金融数据分析应用。
一、分析流程
-
行业选择 → 确定分析范围
-
数据获取 → 收集行业指数、个股交易、财务数据
-
可视化展示 → 理解行业和个股走势
-
财务评价 → 基于PCA的综合评分模型
-
组合构建 → 选择得分最高的股票
-
回测验证 → 计算历史收益率
-
基准比较 → 评估策略超额收益
二、数据准备

数据来源:Tushare数据
fin_data.csv文件字段:

index_trdata.csv文件字段:

stk_trdata.csv文件字段:

复权交易数据2023、2024、2025.csv文件字段:

股票基本信息表.xlsx文件字段:

上市公司基本信息.xlsx文件字段:

沪深300指数交易数据.xlsx文件字段:

最新个股申万行业分类(完整版-截至7月末).xlsx字段:

三、技术栈
-
pandas:处理金融时间序列数据 -
matplotlib:可视化市场走势和个股表现 -
sklearn:应用机器学习方法进行股票评价 -
streamlit:构建分析师或投资者使用的交互工具
import pandas as pd # 数据分析的核心库,用于处理表格数据
import matplotlib.pyplot as plt # 绘图库,用于可视化
from sklearn.preprocessing import StandardScaler # 数据标准化工具
from sklearn.decomposition import PCA # 主成分分析,用于降维和综合评价
import streamlit as st # Web应用框架,用于创建交互界面
四、数据获取与处理函数
4.1 全局设置
确保图表中的中文行业名称、股票名称能正常显示,负的收益率等数据能正确展示。
plt.rcParams['font.sans-serif'] = 'SimHei' # 解决中文显示
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示
4.2 st_data函数:数据获取与整合
def st_data(nm, info):
"""
功能:获取指定行业的数据并进行初步处理,从多个数据源整合信息,形成完整的行业分析基础
nm: 行业名称(如"医药生物"、"电子"等)
info: 行业分类信息表
"""
1. 读取数据
index_trdata: 了解行业整体走势;stk_trdata: 分析行业内个股表现; fin_data: 基本面分析的基础;co_data: 了解公司背景和属性。
# 读取三个核心数据文件
data = pd.read_csv('index_trdata.csv') # 行业指数日度数据
trdata = pd.read_csv('stk_trdata.csv') # 个股日度交易数据
findata = pd.read_csv('fin_data.csv') # 年度财务数据
co_data = pd.read_excel('上市公司基本信息.xlsx') # 公司基本信息
2. 筛选行业指数数据
#筛选指定行业的行业指数交易数据(大于600条)
data_i = data[data['name'] == nm].sort_values('trade_date') # 按时间排序,形成时间序列
data_i.columns = ['指数代码', '行业名称', '交易日期', '开盘指数', '收盘指数', '成交量', '市盈率', '市净率']
if len(data_i) <= 600: # 足够的数据量才能进行有效的趋势分析和统计检验,避免小样本带来的统计偏差
st.warning(f"【{nm}】行业指数数据不足600条(当前{len(data_i)}条)")
return None
3. 绘制行业指数走势图
便于观察行业的长期趋势:上升/下降/震荡 ,识别关键转折点 ,判断行业周期位置,为个股分析提供行业背景。
f1, ax = plt.subplots(figsize=(8, 4))
ax.plot(range(len(data_i)), data_i['收盘指数'], color='#1f77b4')
ax.set_title(f'申万{nm}行业指数走势图', fontsize=12, pad=10)
# 设置x轴刻度:选择7个关键时间点
ax.set_xticks([0, 100, 200, 300, 400, 500, 600])
ax.set_xticklabels(data_i['交易日期'].iloc[[0, 100, 200, 300, 400, 500, 600]],
rotation=45)
ax.grid(alpha=0.3) # 添加网格线,透明度0.3
4. 筛选行业内A股公司
限定A股是为了确保数据可比性和监管一致性,获取行业成分股。
# 筛选条件:属于指定行业且在A股上市
info_filtered = info[(info['新版一级行业'] == nm) & (info['交易所'] == 'A股')]
# 提取股票代码和名称
chy_code = info_filtered.iloc[:, [2, 3]] # 按位置索引获取特定列
chy_code.columns = ['ts_code', 'nm'] # 统一列名
5. 数据合并与整合
# 合并公司基本信息与行业代码
co_data = pd.merge(co_data, chy_code, how='inner', on='ts_code') # inner join: 只保留同时存在于两个表中的数据
# 合并交易数据与行业代码
trdata_hy = pd.merge(trdata, chy_code, how='inner', on='ts_code')
trdata_hy = trdata_hy.sort_values(['ts_code', 'trade_date']) # 按股票和时间排序,sort_values: 确保时间序列的正确性
# 重命名列
trdata_hy.columns = ['股票代码', '交易日期', '收盘价', '成交量',
'成交金额', '股票简称']
6. 绘制个股走势图
# 创建3行2列的子图
f2, axs = plt.subplots(3, 2, figsize=(12, 8))
axs = axs.flatten() # 将多维数组展平为一维,便于循环
code_list = trdata_hy['股票代码'].unique() # 获取唯一的股票代码列表
p = 0 # 计数器,控制绘制6只股票
for code in code_list:
trdata_k = trdata_hy[trdata_hy['股票代码'] == code]
# 条件:数据量足够且不超过6只
if len(trdata_k) > 600 and p < 6:
axs[p].plot(range(len(trdata_k)), trdata_k['收盘价'], color='#ff7f0e')
axs[p].set_title(trdata_k['股票简称'].iloc[0], fontsize=10)
# 设置x轴刻度(与行业指数图对齐)
axs[p].set_xticks([0, 100, 200, 300, 400, 500, 600])
axs[p].set_xticklabels(trdata_k['交易日期'].iloc[[0, 100, 200, 300, 400, 500, 600]], rotation=45)
axs[p].grid(alpha=0.2)
p += 1
# 隐藏多余的子图
while p < 6:
axs[p].set_visible(False)
p += 1
plt.tight_layout() # 自动调整子图间距
7. 财务数据整理
获取行业内所有公司的财务数据 ,为后续的财务分析提供基础。
# 提取行业内的股票代码
code_p = pd.DataFrame({'股票代码': code_list})
# 合并财务数据与行业股票代码
findata_m = pd.merge(findata, code_p, how='inner', on='股票代码')
五、综合评价函数Fr
主要做基于财务数据的股票评分。
def Fr(data, year):
"""
功能:使用主成分分析(PCA)对股票进行综合评价,将多个财务指标综合成一个评分,用于股票排序和选择
data: 财务数据
year: 评价年度
"""
1. 数据准备与校验
确保分析的时效性(使用最新财务数据),避免跨年数据混合导致的失真。
# 检查数据是否为空
if data.empty:
return pd.DataFrame(columns=['股票代码', '股票简称', '综合得分'])
# 筛选指定年度的数据
tdata = data[data['年度'] == year]
if tdata.empty:
st.warning(f"无{year}年度财务数据")
return pd.DataFrame(columns=['股票代码', '股票简称', '综合得分'])
2. 数据清洗
· 删除负值:某些指标为负可能表示公司存在问题;
· 删除空值:确保分析的完整性和准确性;
· 数据质量直接影响模型效果。
# 提取财务指标(排除股票代码和年份)
data_x = tdata.iloc[:, 1:-1]
# 过滤负值(财务指标通常应为正值)
data_x = data_x[data_x > 0]
# 保留股票代码用于后续合并
data_x['股票代码'] = tdata['股票代码'].values
# 删除包含空值的行
data_x = data_x.dropna()
3. 数据标准化
目的是消除量纲影响,不同财务指标单位不同(元、百分比、倍数等),另外也要使所有指标具有可比性 ,标准化后所有指标均值为0,标准差为1,这是PCA分析的必要前提。
# 分离特征(财务指标)和标签(股票代码)
X = data_x.iloc[:, :-1]
# 创建标准化器
scaler = StandardScaler()
# 标准化处理
X_scaled = scaler.fit_transform(X)
4. 主成分分析(PCA)
采用降维的方法,将多个相关财务指标转换为少数几个独立的主成分,保留最重要的财务特征 ,消除多重共线性。
# 创建PCA模型,保留95%的原始信息
pca = PCA(n_components=0.95)
# 对标准化后的数据进行PCA转换
Y = pca.fit_transform(X_scaled)
# 获取各主成分的方差贡献率:表示每个主成分解释原始数据变异的能力
gxl = pca.explained_variance_ratio_
5. 计算综合得分
# 计算综合得分:各主成分得分加权求和
F = (Y * gxl).sum(axis=1)
6. 整合结果
try:
# 读取股票基本信息(获取股票简称)
stk_data = pd.read_excel('股票基本信息表.xlsx')[['ts_code', 'name']]
stk_data.columns = ['股票代码', '股票简称']
# 合并股票简称和综合得分
nm_stk_data = pd.merge(stk_data, data_x[['股票代码']], on='股票代码')
nm_stk_data['综合得分'] = F
# 按综合得分降序排序
nm_stk_data = nm_stk_data.sort_values('综合得分', ascending=False).reset_index(drop=True)
except:
# 异常处理:如果无法获取股票简称,使用空名称
nm_stk_data = pd.DataFrame({
'股票代码': data_x['股票代码'].values,
'股票简称': '',
'综合得分': F
}).sort_values('综合得分', ascending=False).reset_index(drop=True)
六、收益率计算函数Tr
主要是做投资组合收益回测。
def Tr(rdata, rank, date1, date2):
"""
功能:计算投资组合在指定期间的收益率,主要用于验证财务评价模型的有效性,计算历史表现
rdata: 股票排名数据(来自Fr函数)
rank: 投资组合中股票数量
date1: 开始日期
date2: 结束日期
"""
1. 数据准备
# 读取三年的复权交易数据
A1 = pd.read_csv('复权交易数据2023.csv')
A2 = pd.read_csv('复权交易数据2024.csv')
A3 = pd.read_csv('复权交易数据2025.csv')
# 合并数据
A = pd.concat([A1, A2, A3])
# 确保日期为字符串格式,便于比较
A['trade_date'] = A['trade_date'].astype(str)
# 将datetime对象转换为字符串格式
date1_str = date1.strftime('%Y%m%d')
date2_str = date2.strftime('%Y%m%d')
2. 构建投资组合
选择综合得分最高的股票进行构建组合。
# 获取排名前rank的股票代码
top_stk = rdata.head(rank)['股票代码'].tolist()
# 初始化收益率列表和总收益率
stock_ret_list = [] # 存储每只股票的收益率
r = 0.0 # 投资组合总收益率
3. 计算单只股票收益率
持有期收益率 = (期末价-期初价)/期初价
等权重组合收益率 = 各股票收益率的算术平均
for code in top_stk:
# 筛选指定期间内该股票的数据
stk_data = A[
(A['ts_code'] == code) &
(A['trade_date'] >= date1_str) &
(A['trade_date'] <= date2_str)
].sort_values('trade_date') # 按时间排序
# 检查数据是否足够(至少有两个交易日)
if len(stk_data) >= 2:
p1 = stk_data['close'].iloc[0] # 期初价格
p2 = stk_data['close'].iloc[-1] # 期末价格
# 计算简单收益率
ri = (p2 - p1) / p1
# 累加到总收益率(等权重假设)
r += ri
# 记录单只股票收益率
stock_ret_list.append({'股票代码': code, '单只收益率': f"{ri*100:.2f}%"})
else:
# 数据不足的情况
stock_ret_list.append({'股票代码': code, '单只收益率': "数据不足"})
4. 计算市场基准收益率
# 读取沪深300指数数据
hs300 = pd.read_excel('沪深300指数交易数据.xlsx')
hs300['trade_date'] = hs300['trade_date'].astype(str)
# 筛选指定期间的数据
hs300_data = hs300[
(hs300['trade_date'] >= date1_str) &
(hs300['trade_date'] <= date2_str)
].sort_values('trade_date')
# 计算沪深300收益率
rv = 0.0
if len(hs300_data) >= 2:
rv = (hs300_data['close'].iloc[-1] - hs300_data['close'].iloc[0]) / hs300_data['close'].iloc[0]
5. 返回结果
stock_ret_df::分析组合内部分化情况
r::投资组合整体表现
rv:市场基准表现
# 创建单只股票收益率的DataFrame
stock_ret_df = pd.DataFrame(stock_ret_list)
# 返回:单只收益率表、组合总收益率、基准收益率
return stock_ret_df, round(r, 4), round(rv, 4) # round(r, 4):保留4位小数
七、Streamlit界面设计
7.1 页面配置
def st_fig():
# 设置页面配置
st.set_page_config(
page_title="申万行业数据分析", # 浏览器标签页标题
layout='wide', # 宽屏布局,充分利用空间
initial_sidebar_state='expanded' # 侧边栏默认展开
)
7.2 侧边栏
with st.sidebar:
st.title("申万行业分析")
st.divider() # 添加分割线
# 读取行业分类表
try:
info = pd.read_excel('最新个股申万行业分类(完整版-截至7月末).xlsx')
nm_L = sorted(info['新版一级行业'].unique()) # 获取唯一的行业列表并排序
except:
st.error("行业分类表读取失败!请检查文件路径和名称")
return
# 行业选择下拉框
nm = st.selectbox("选择行业", nm_L, index=0)
st.divider()
# 评价参数设置
st.subheader("评价参数")
year = st.selectbox("评价年度", [2022, 2023, 2024], index=1) # 默认选择2023
rank = st.selectbox("投资组合数量", [5, 10, 15, 20], index=0) # 默认5只
st.divider()
# 持有期选择
st.subheader("持有期")
date1 = st.date_input("开始日期", value=pd.to_datetime('2023-01-01'))
date2 = st.date_input("结束日期", value=pd.to_datetime('2023-12-31'))
7.3 主界面
# 主标题
st.header(f"申万{nm}行业数据分析", divider='blue')
# 调用数据处理函数
result = st_data(nm, info)
if not result:
return
# 解包返回结果
f1, f2, data_i, findata_m, trdata_hy, co_data = result
7.4 双列布局展示图表
# 创建两列布局
col1, col2 = st.columns(2)
with col1:
st.subheader("指数走势图")
st.pyplot(f1) # 展示行业指数走势图
with col2:
st.subheader("前6只股票价格走势图")
st.pyplot(f2) # 展示个股走势图
7.5 可折叠数据表格
# 添加分割线
st.divider()
# 创建可展开/折叠的数据表格
with st.expander("指数交易数据", expanded=False):
st.dataframe(data_i, use_container_width=True, height=300)
with st.expander("上市公司基本信息", expanded=False):
st.dataframe(co_data, use_container_width=True, height=300)
with st.expander("股票财务数据", expanded=False):
st.dataframe(findata_m, use_container_width=True, height=300)
with st.expander("股票交易数据(前2000条)", expanded=False):
st.dataframe(trdata_hy.iloc[:2000], use_container_width=True, height=300)
7.6 综合评价结果展示
st.divider()
st.subheader("综合评价结果")
# 调用综合评价函数
eval_result = Fr(findata_m, year)
if eval_result.empty:
# 无数据时显示空表格
st.dataframe(pd.DataFrame({'股票代码': [], '股票简称': [], '综合得分': []}),
use_container_width=True)
else:
# 展示评价结果
st.dataframe(eval_result, use_container_width=True, height=300)
# 添加分割线
st.divider()
# 绘制前rank只股票的得分折线图
st.subheader(f"前{rank}只股票综合得分折线图")
# 提取前rank个股票
top_rank_data = eval_result.head(rank)
# 创建折线图
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(top_rank_data['股票简称'], top_rank_data['综合得分'],
marker='o', color='#2ca02c', linewidth=2)
# 添加数据标签
for i, score in enumerate(top_rank_data['综合得分']):
ax.text(i, score + 0.1, f"{score:.2f}", ha='center')
# 设置图表样式
ax.set_title(f"前{rank}只股票综合得分分布", fontsize=12)
ax.set_xlabel("股票简称", fontsize=9, fontweight='normal')
ax.set_ylabel("综合得分", fontsize=9, fontweight='normal')
ax.tick_params(axis='x', rotation=45) # 旋转x轴标签
ax.grid(alpha=0.3)
# 自动调整布局并显示
plt.tight_layout()
st.pyplot(fig)
7.7 收益率分析展示
st.divider()
st.subheader("收益率分析")
if not eval_result.empty:
# 调用收益率计算函数
stock_ret_df, r, rv = Tr(eval_result, rank, date1, date2)
# 展示单只股票收益率
st.write("单只股票收益率:")
st.dataframe(stock_ret_df, use_container_width=True)
# 双列布局展示关键指标
col1, col2 = st.columns(2)
with col1:
# 投资组合总收益率
st.metric("投资组合总收益率", f"{r*100:.2f}%")
with col2:
# 沪深300同期收益率
st.metric("沪深300同期收益率", f"{rv*100:.2f}%")
else:
st.write("暂无有效数据计算收益率")
八、项目启动与执行流程
if __name__ == "__main__":
st_fig() # 启动Streamlit应用
九、Streamlit简单页面展示

详细页面展示:

十、系统可扩展性
这个项目提供了一个完整的金融数据分析框架,采用模块化的架构设计,各功能模块之间保持松耦合关系,便于后面的独立开发与扩展。
想要部署Streamlit网页的可详见这篇:飞桨AI Studio部署Streamlit全攻略(Python)-优快云博客
7150

被折叠的 条评论
为什么被折叠?



