金融数据分析-申万行业数据分析系统(Python+Streamlit)

        本文介绍一个申万行业数据分析系统,它集成了数据获取、可视化、财务评价和回测分析等功能。通过这个项目,你将学会如何用Python构建一个专业级的金融数据分析应用。

一、分析流程

  1. 行业选择 → 确定分析范围

  2. 数据获取 → 收集行业指数、个股交易、财务数据

  3. 可视化展示 → 理解行业和个股走势

  4. 财务评价 → 基于PCA的综合评分模型

  5. 组合构建 → 选择得分最高的股票

  6. 回测验证 → 计算历史收益率

  7. 基准比较 → 评估策略超额收益

二、数据准备

数据来源: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)-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值