python Streamlit和AKShare 实现的股票数据查询系统

在这里插入图片描述

1. 系统概述

这是一个基于Streamlit和AKShare的股票数据查询系统,提供了便捷的股票数据查询和可视化功能。系统支持按板块筛选股票、多股票代码查询、数据导出等功能。

1.1 主要功能

  • 股票代码直接输入查询
  • 按板块筛选和选择股票
  • 历史数据和实时行情查询
  • 财务报表数据获取
  • 新闻公告展示
  • 数据导出Excel

1.2 技术栈

  • 前端框架: Streamlit
  • 数据源: AKShare
  • 数据处理: Pandas
  • 数据导出: OpenPyXL

2. 系统架构

2.1 模块组织

stock.py
├── 数据获取模块
│   ├── get_sector_list()    # 获取板块列表
│   ├── get_sector_stocks()  # 获取板块成分股
│   └── get_stock_data()     # 获取股票详细数据
├── 数据处理模块
│   └── save_to_excel()      # 数据导出Excel
└── 主界面模块
    └── main()              # 主程序入口

2.2 状态管理

使用Streamlit的Session State管理以下状态:

st.session_state:
├── stock_codes_input  # 股票代码输入
├── selected_stocks    # 已选择的股票列表
├── sector_stocks_df   # 板块成分股数据
├── start_date        # 开始日期
├── end_date          # 结束日期
└── reset_sector      # 板块重置标志

3. 核心功能实现

3.1 板块股票选择

# 获取板块列表
sector_list = get_sector_list()
sector_name = st.selectbox("选择板块", options=sector_list)

# 获取板块成分股
if sector_name:
    sector_stocks = get_sector_stocks(sector_name)
    selected_stocks = st.multiselect(
        "选择要查看的股票",
        options=sector_stocks['代码'].tolist(),
        format_func=lambda x: f"{x} ({sector_stocks[sector_stocks['代码']==x]['名称'].iloc[0]})"
    )

3.2 数据获取

def get_stock_data(stock_codes, sector_name, start_date, end_date):
    results = {}
    
    # 获取个股历史数据
    stock_hist = ak.stock_zh_a_hist(
        symbol=code, 
        period="daily",
        start_date=start_date, 
        end_date=end_date
    )
    
    # 获取实时行情
    stock_real = ak.stock_zh_a_spot_em()
    
    # 获取财务数据
    financial_data = ak.stock_financial_report_sina(
        stock=code, 
        symbol="资产负债表"
    )
    
    return results

3.3 界面布局

使用Streamlit的列布局实现左右分栏:

left_column, right_column = st.columns([1, 3])  # 1:3的宽度比例

with left_column:
    # 查询参数输入区
    st.header("查询参数")
    ...

with right_column:
    # 数据显示区
    st.markdown("### 数据显示")
    ...

4. 使用流程

  1. 选择板块

    • 从下拉列表选择股票板块
    • 系统自动获取并显示板块成分股列表
  2. 选择股票

    • 从成分股列表中选择感兴趣的股票
    • 选中的股票代码自动添加到输入框
    • 板块选择自动重置为空
  3. 设置查询参数

    • 设置查询日期范围
    • 可以手动添加或修改股票代码
  4. 获取数据

    • 点击"获取数据"按钮
    • 系统获取并显示所选股票的详细数据
    • 可以下载数据到Excel文件

5. 数据展示

5.1 成分股列表

  • 使用表格展示完整的成分股信息
  • 支持多选操作
  • 实时显示已选股票列表

5.2 股票数据

  • 历史行情数据
  • 实时市场数据
  • 财务报表数据
  • 相关新闻公告

5.3 新闻展示

  • 新闻标题和发布时间双列布局
  • 支持点击标题跳转到新闻详情
  • 按时间顺序排列

6. 注意事项

  1. 数据刷新

    • 实时数据每次查询都会更新
    • 历史数据基于选择的日期范围
  2. 性能优化

    • 使用缓存减少重复API调用
    • 批量处理多个股票的数据请求
  3. 错误处理

    • 对API调用异常进行捕获和提示
    • 确保数据完整性和显示正确性

7. 后续优化方向

  1. 数据分析

    • 添加技术分析指标
    • 实现数据可视化图表
  2. 用户体验

    • 添加数据加载进度条
    • 优化大量数据的显示效果
  3. 功能扩展

    • 添加自选股票组合
    • 实现数据监控告警
    • 支持更多类型的数据导出

8. 依赖安装

pip install streamlit
pip install akshare
pip install pandas
pip install openpyxl

9. 运行方式

streamlit run stock.py

10. 代码示例

import streamlit as st
import akshare as ak
import pandas as pd
from datetime import datetime, timedelta
import io

@st.cache_data(ttl=3600)  # 缓存板块数据1小时
def get_sector_list():
    """获取所有板块名称列表"""
    try:
        sector_data = ak.stock_board_industry_name_em()
        sector_list = sector_data['板块名称'].tolist()
        return [''] + sector_list  # 添加空选项作为默认值
    except Exception as e:
        st.error(f"获取板块列表时出错: {str(e)}")
        return ['']

def get_sector_stocks(sector_name):
    """获取板块内的所有股票"""
    try:
        # 获取板块成分股
        stocks = ak.stock_board_industry_cons_em(symbol=sector_name)
        return stocks
    except Exception as e:
        st.error(f"获取板块成分股时出错: {str(e)}")
        return pd.DataFrame()

def get_stock_data(stock_codes, sector_name, start_date, end_date):
    """获取股票数据"""
    results = {}
    
    # 1. 获取股票数据
    if stock_codes:
        for code in stock_codes:
            try:
                # 获取个股历史数据
                stock_hist = ak.stock_zh_a_hist(symbol=code, period="daily", 
                                              start_date=start_date, end_date=end_date)
                results[f"股票{code}历史数据"] = stock_hist
                
                # 获取个股实时行情
                stock_real = ak.stock_zh_a_spot_em()
                stock_info = stock_real[stock_real['代码'] == code]
                results[f"股票{code}实时行情"] = stock_info
                
                # 获取财务报表数据
                try:
                    financial_data = ak.stock_financial_report_sina(stock=code, symbol="资产负债表")
                    results[f"股票{code}财务数据"] = financial_data
                except:
                    st.warning(f"获取股票{code}财务数据失败")
            except Exception as e:
                st.error(f"获取股票{code}数据时出错: {str(e)}")
    
    # 2. 获取板块数据
    if sector_name:
        try:
            # 获取板块数据
            sector_data = ak.stock_board_industry_name_em()
            sector_data = sector_data[sector_data['板块名称'] == sector_name]
            results["板块数据"] = sector_data
            
            # 获取板块成分股列表
            sector_stocks = get_sector_stocks(sector_name)
            if not sector_stocks.empty:
                results["板块成分股列表"] = sector_stocks
        except Exception as e:
            st.error(f"获取板块数据时出错: {str(e)}")
    
    # 3. 获取指数数据
    try:
        # 获取上证指数数据并按照时间筛选
        sh_index = ak.stock_zh_index_daily(symbol="sh000001")
        sh_index['日期'] = pd.to_datetime(sh_index['date'])  # 确保日期格式一致
        start_datetime = pd.to_datetime(start_date)
        end_datetime = pd.to_datetime(end_date)
        sh_index = sh_index[
            (sh_index['日期'] >= start_datetime) & 
            (sh_index['日期'] <= end_datetime)
        ]
        if not sh_index.empty:
            results["上证指数数据"] = sh_index
        else:
            st.warning("选定时间范围内没有上证指数数据")
            
        # 获取深证指数数据并按照时间筛选
        sz_index = ak.stock_zh_index_daily(symbol="sz399001")
        sz_index['日期'] = pd.to_datetime(sz_index['date'])
        sz_index = sz_index[
            (sz_index['日期'] >= start_datetime) & 
            (sz_index['日期'] <= end_datetime)
        ]
        if not sz_index.empty:
            results["深证指数数据"] = sz_index
        else:
            st.warning("选定时间范围内没有深证指数数据")
    except Exception as e:
        st.error(f"获取指数数据时出错: {str(e)}")
    
    # 4. 获取新闻公告
    try:
        news_data = ak.stock_news_em()
        # 确保新闻数据不为空且包含必要的列
        if not news_data.empty and '新闻标题' in news_data.columns and '新闻链接' in news_data.columns and '发布时间' in news_data.columns:
            # 只保留最新的20条新闻
            news_data = news_data.head(20)
            results["新闻公告"] = news_data
        else:
            st.warning("暂无新闻数据")
    except Exception as e:
        st.error(f"获取新闻数据时出错: {str(e)}")
    
    return results

def save_to_excel(data_dict):
    """将数据保存到Excel文件"""
    output = io.BytesIO()
    with pd.ExcelWriter(output, engine='openpyxl') as writer:
        for sheet_name, df in data_dict.items():
            if not df.empty:
                df.to_excel(writer, sheet_name=sheet_name[:31])  # Excel sheet名称最大31字符
    
    output.seek(0)
    return output

def main():
    st.set_page_config(layout="wide")  # 设置宽屏模式
    
    # 创建左右两列布局
    left_column, right_column = st.columns([1, 3])  # 1:3的宽度比例
    
    # 初始化session state
    if 'stock_codes_input' not in st.session_state:
        st.session_state.stock_codes_input = ""
    if 'selected_stocks' not in st.session_state:
        st.session_state.selected_stocks = []
    if 'sector_stocks_df' not in st.session_state:
        st.session_state.sector_stocks_df = None
    if 'start_date' not in st.session_state:
        st.session_state.start_date = (datetime.now() - timedelta(days=30)).strftime("%Y%m%d")
    if 'end_date' not in st.session_state:
        st.session_state.end_date = datetime.now().strftime("%Y%m%d")
    if 'reset_sector' not in st.session_state:
        st.session_state.reset_sector = False
    
    # 左侧边栏:输入参数
    with left_column:
        st.header("查询参数")
        
        # 股票代码输入
        stock_codes_input = st.text_input(
            "股票代码(用空格分隔)",
            value=st.session_state.stock_codes_input,
            help="沪市股票代码以60、68开头,如:600000(浦发银行)、688001(华兴源创)\n"
                 "深市股票代码以00、30开头,如:000001(平安银行)、300059(东方财富)、301469(德视佳)\n"
                 "多个股票代码用空格分隔,例如:600000 000001 300059",
            key="stock_input"
        )
        stock_codes = [code.strip() for code in stock_codes_input.split() if code.strip()]
        
        # 板块名称下拉选择
        sector_list = get_sector_list()
        sector_index = 0 if st.session_state.reset_sector else None
        sector_name = st.selectbox(
            "选择板块",
            options=sector_list,
            index=sector_index,  # 如果需要重置,则选择第一个空选项
            help="选择要查询的板块,不选则不获取板块数据",
            key="sector_selector"
        )
        # 重置标志
        st.session_state.reset_sector = False
        
        # 日期选择
        col1, col2 = st.columns(2)
        with col1:
            start_date = st.date_input(
                "开始日期",
                datetime.strptime(st.session_state.start_date, "%Y%m%d")
            )
            st.session_state.start_date = start_date.strftime("%Y%m%d")
            
        with col2:
            end_date = st.date_input(
                "结束日期",
                datetime.strptime(st.session_state.end_date, "%Y%m%d")
            )
            st.session_state.end_date = end_date.strftime("%Y%m%d")
        
        # 如果选择了板块,立即获取成分股列表
        if sector_name and sector_name != sector_list[0]:
            with st.spinner("正在获取板块成分股..."):
                sector_stocks = get_sector_stocks(sector_name)
                if not sector_stocks.empty:
                    st.session_state.sector_stocks_df = sector_stocks
                    
                    # 在右侧显示成分股列表
                    with right_column:
                        st.markdown("### 板块成分股列表")
                        # 创建两列:一列用于显示表格,一列用于显示选中的股票信息
                        table_col, info_col = st.columns([2, 1])
                        with table_col:
                            # 添加多选框
                            selected_stocks = st.multiselect(
                                "选择要查看的股票",
                                options=sector_stocks['代码'].tolist(),
                                format_func=lambda x: f"{x} ({sector_stocks[sector_stocks['代码']==x]['名称'].iloc[0]})",
                                key="stock_selector"
                            )
                            
                            # 当选择股票时,立即更新股票代码输入框并重置板块选择
                            if selected_stocks != st.session_state.selected_stocks:
                                st.session_state.selected_stocks = selected_stocks
                                # 将新选择的股票代码添加到现有代码中
                                current_codes = set(stock_codes)
                                new_codes = set(selected_stocks)
                                all_codes = current_codes.union(new_codes)
                                st.session_state.stock_codes_input = " ".join(sorted(all_codes))
                                # 设置重置板块选择的标志
                                st.session_state.reset_sector = True
                                st.experimental_rerun()
                            
                            st.dataframe(sector_stocks, use_container_width=True)
                        
                        with info_col:
                            if selected_stocks:
                                st.markdown("#### 已选股票")
                                selected_df = sector_stocks[sector_stocks['代码'].isin(selected_stocks)]
                                for _, row in selected_df.iterrows():
                                    st.markdown(f"- {row['代码']} ({row['名称']})")
        
        # 添加分隔线
        st.markdown("---")
        
        # 获取数据按钮
        if st.button("获取数据", use_container_width=True, key="query_button"):
            if not stock_codes and not sector_name:
                st.warning("请至少输入一个股票代码或选择板块")
                return
            
            with st.spinner("正在获取数据..."):
                # 获取数据
                results = get_stock_data(stock_codes, sector_name, st.session_state.start_date, st.session_state.end_date)
                
                # 生成Excel文件下载按钮
                if results:
                    excel_data = save_to_excel(results)
                    st.download_button(
                        label="下载Excel文件",
                        data=excel_data,
                        file_name=f"stock_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx",
                        mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                        use_container_width=True
                    )
                    
                    # 在右侧显示数据
                    with right_column:
                        # 显示所有数据
                        for name, df in results.items():
                            if not df.empty:
                                if name == "新闻公告":
                                    st.markdown("### 新闻公告")
                                    # 为新闻添加可点击的链接
                                    col1, col2 = st.columns([7, 3])
                                    with col1:
                                        st.write("新闻标题")
                                    with col2:
                                        st.write("发布时间")
                                    
                                    for _, row in df.iterrows():
                                        col1, col2 = st.columns([7, 3])
                                        with col1:
                                            st.markdown(f"- [{row['新闻标题']}]({row['新闻链接']})")
                                        with col2:
                                            st.write(row['发布时间'])
                                else:
                                    st.markdown(f"### {name}")
                                    st.dataframe(df, use_container_width=True)
                                st.markdown("---")

if __name__ == '__main__':
    main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/954304.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯备赛:顺序表和单链表相关算法题详解(上)

目录 一.询问学号&#xff08;顺序表&#xff09; 1.题目来源&#xff1a; 2.解析与代码实现&#xff1a; &#xff08;1&#xff09;解析&#xff1a; &#xff08;2&#xff09;代码实现&#xff1a; 二.寄包柜&#xff08;顺序表&#xff09; 1.题目来源&#xff1a; …

数据结构-ArrayLIst-一起探索顺序表的底层实现

各位看官早安午安晚安呀 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 大家好&#xff0c;我们今天来学习java数据结构的第一章ArrayList&#xff08;顺序表&#xff09; 1.ArrayList的概念 那小伙伴就要问了线性表到…

RabbitMQ(四)

SpringBoot整合RabbitMQ SpringBoot整合1、生产者工程①创建module②配置POM③YAML④主启动类⑤测试程序 2、消费者工程①创建module②配置POM③YAML文件内配置&#xff1a; ④主启动类⑤监听器 3、RabbitListener注解属性对比①bindings属性②queues属性 SpringBoot整合 1、生…

初始Java4

目录 一.继承 1.定义&#xff1a; 2.继承的语法&#xff1a; 3.子类访问父类 4.子类构造方法 5.super与this 6.继承方法 7.final关键字 &#xff08;1&#xff09;.变量不变 &#xff08;2&#xff09;.方法不变 &#xff08;3&#xff09;.类不可继承 8.继承与组合…

极限竞速 地平线5“d3dx12_43.dll”文件丢失或错误导致游戏运行异常如何解决?windows系统DLL文件修复方法

d3dx12_43.dll是存放在windows系统中的一个重要dll文件&#xff0c;缺少它可能会造成部分软件不能正常运行。当你的电脑弹出提示“无法找到d3dx12_43.dll”或“计算机缺少d3dx12_43.dll”等错误问题&#xff0c;请不用担心&#xff0c;我们将深入解析DLL文件错误的成因&#xf…

Leecode刷题C语言之超过阈值的最小操作数②

执行结果:通过 执行用时和内存消耗如下&#xff1a; // 最小堆的节点结构体 typedef struct {long long* heap;int size;int capacity; } MinHeap;// 初始化最小堆 MinHeap* createMinHeap(int capacity) {MinHeap* minHeap (MinHeap*)malloc(sizeof(MinHeap));minHeap->s…

[Qt]常用控件介绍-按钮类控件-QPushButton、QRedioButton、QCheckBox、QToolButton控件

目录 1.QPushButton按钮 介绍 属性 Demo&#xff1a;键盘方向键控制人物移动 2.Redio Button按钮 属性 clicked、pressed、released、toggled区别 单选按钮的分组 Demo&#xff1a;点餐小程序 3.CheckBox按钮 属性 Demo&#xff1a;获取今天的形成计划 4.ToolBu…

寒假第一次牛客周赛 Round 76回顾

AC数&#xff1a;2&#xff08;A、C&#xff09; B 思路&#xff1a; 等价于求&#xff1a; 数量最多的字符 #include<stdio.h> int main() {int n,num;int a[26]{0};//用于存储字母 a 到 z 的出现次数。scanf("%d",&n);char s[n];scanf("%s",s)…

StyleGaussian: Instant 3D Style Transferwith Gaussian Splatting 论文解读

目录 一、概述 二、相关工作 1、辐射场 2、3D编辑 3、风格迁移 三、StyleGaussian 1、特征嵌入 2、风格迁移 3、解码 四、实验 1、不同backbone下的量化和定性指标 2、解码器设计上的测试 3、内容损失平衡 4、风格平滑插值 一、概述 提出了StyleGaussian&#x…

基于django实现类似ebay的电子商务系统全英文

完整源码项目包获取→点击文章末尾名片&#xff01;

win32汇编环境,窗口程序中组合框的应用举例

;运行效果 ;win32汇编环境,窗口程序中组合框的应用举例 ;比如在窗口程序中生成组合框&#xff0c;增加子项&#xff0c;删除某项&#xff0c;取得指定项内容等 ;直接抄进RadAsm可编译运行。重点部分加备注。 ;以下是ASM文件 ;>>>>>>>>>>>>…

Docker 镜像制作原理 做一个自己的docker镜像

一.手动制作镜像 启动容器进入容器定制基于容器生成镜像 1.启动容器 启动容器之前我们首先要有一个镜像&#xff0c;这个镜像可以是从docker拉取&#xff0c;例如&#xff1a;现在pull一个ubuntu镜像到本机。 docker pull ubuntu:22.04 我们接下来可以基于这个容器进行容器…

微信小程序获取openid

2025年1月15日&#xff1a; 1、现在云服务器上安装nodejs&#xff0c;然后写个get接口&#xff1a; const express require(express); const app express();app.get(/getOpenid,(req,res)>{res.send("success"); })app.listen(3000,()>{console.log(server…

ASP.NET Core - 配置系统之配置添加

ASP.NET Core - 配置系统之配置添加 2. 配置添加 2. 配置添加 配置系统可以读取到配置文件中的信息&#xff0c;那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到 ASP.NET Core 入口文件中&#xff0c;builder(WebApplicationBuilder 对象) 中有一个 Config…

C#中通道(Channels)的应用之(生产者-消费者模式)

一.生产者-消费者模式概述 生产者-消费者模式是一种经典的设计模式&#xff0c;它将数据的生成&#xff08;生产者&#xff09;和处理&#xff08;消费者&#xff09;分离到不同的模块或线程中。这种模式的核心在于一个共享的缓冲区&#xff0c;生产者将数据放入缓冲区&#x…

ArcSegment绘制及计算

ArcSegment绘制及计算 给定起始点、终止点和 bulge 值计算弧线中心点和半径&#xff0c;绘制ArcSegment。 import math def calculate_arc_center_and_radius(x1, y1, x2, y2, bulge):angle4*math.atan(bulge)# 计算弦中点mid_x (x1 x2) / 2mid_y (y1 y2) / 2# 计算弦长的…

【高可用自动化体系】自动化体系

架构设计的愿景就是高可用、高性能、高扩展、高效率。为了实现架构设计四高愿景&#xff0c;需要实现自动化系统目标&#xff1a; 标准化。 流程自助化。 可视化&#xff1a;可观测系统各项指标、包括全链路跟踪。 自动化&#xff1a;ci/cd 自动化部署。 精细化&#xff1a…

FakeLocation 1599 | 内部旧版

前言:FakeLocation又更新了,在某安上面看见一些&#xff0c;大概问题就是地图没了&#xff0c;然后有更难搞了 任务一 我们先去看看地图是怎么个事情 这里用的是百度地图就没有了哈 高德地图是有的 任务二 null 选择成功了&#xff0c;虽然是null 任务三 地图位置 虽然不显示了…

初识算法和数据结构P1:保姆级图文详解

文章目录 前言1、算法例子1.1、查字典&#xff08;二分查找算法&#xff09;1.2、整理扑克&#xff08;插入排序算法&#xff09;1.3、货币找零&#xff08;贪心算法&#xff09; 2、算法与数据结构2.1、算法定义2.2、数据结构定义2.3、数据结构与算法的关系2.4、独立于编程语言…

2025年华数杯国际赛B题论文首发+代码开源 数据分享+代码运行教学

176项指标数据库 任意组合 千种组合方式 14页纯图 无水印可视化 63页无附录正文 3万字 1、为了方便大家阅读&#xff0c;全文使用中文进行描述&#xff0c;最终版本需自行翻译为英文。 2、文中图形、结论文字描述均为ai写作&#xff0c;可自行将自己的结果发给ai&#xff0c…