news 2026/9/5 10:58:48

Python金融数据分析入门:用Tushare获取股票历史行情(附完整代码示例)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python金融数据分析入门:用Tushare获取股票历史行情(附完整代码示例)

Python金融数据分析入门:用Tushare获取股票历史行情(附完整代码示例)

对于刚接触金融数据分析的Python爱好者来说,最令人兴奋又略带困惑的第一步,往往就是如何获取可靠、结构化的市场数据。市面上充斥着各种数据源,有的需要付费订阅,有的界面复杂,有的数据质量参差不齐。如果你渴望一种更“程序员友好”的方式——通过几行代码就能将海量历史行情数据拉取到本地,进行自由的分析和建模,那么Tushare这个工具库绝对值得你深入了解。它并非一个简单的数据下载器,而是一个将金融数据API化的桥梁,让你能够像调用普通函数一样,获取从股票、基金到宏观经济等一系列金融数据。这篇文章,我将从一个实践者的角度,带你从零开始,手把手掌握Tushare的核心用法,并分享一些我实际使用中总结出的数据处理技巧和避坑指南。

1. 环境准备与Tushare初探

在开始编写任何代码之前,一个稳定、隔离的Python环境是高效工作的基石。我强烈建议使用condavenv创建独立的虚拟环境,这能避免不同项目间的依赖冲突。假设你已经安装了Python(建议3.7及以上版本),那么环境搭建可以这样开始:

# 创建并激活一个名为‘finance’的虚拟环境(以conda为例) conda create -n finance python=3.9 conda activate finance # 安装核心依赖 pip install tushare pandas numpy matplotlib

这里,pandasnumpy是后续数据处理的左膀右臂,matplotlib则用于基础的可视化。安装完成后,你首先需要访问Tushare的官网进行注册,以获取一个唯一的token。这个token是你的身份凭证,所有数据请求都基于它。注册过程完全免费,对于个人学习和研究来说,获取基础数据(如日线行情)的权限已经足够。

拿到token后,我们进行初始化。不要把token硬编码在脚本里,尤其是如果你打算将代码分享到GitHub等平台。一个更安全的做法是将其存储在环境变量中。

import tushare as ts import pandas as pd import os # 方式一:直接在代码中设置(仅用于临时测试,不推荐用于正式项目) ts.set_token('你的token字符串') # 方式二:从环境变量读取(推荐) # 假设你在终端中执行了 `export TUSHARE_TOKEN='你的token'` token = os.getenv('TUSHARE_TOKEN') if token: ts.set_token(token) else: print("请设置环境变量 TUSHARE_TOKEN") exit() # 初始化Pro接口 pro = ts.pro_api()

初始化pro对象后,你就拥有了打开金融数据宝库的钥匙。Tushare的数据通过不同的API函数提供,每个函数对应一类数据。在开始调用前,花几分钟浏览官网的数据字典,了解有哪些数据可用,以及每个API所需的参数,这会让你后续的探索事半功倍。

2. 核心API调用实战:获取个股历史行情

获取数据是第一步,但如何高效、准确地获取所需数据,里面有不少门道。Tushare Pro接口提供了dailyweeklymonthly等函数来获取不同周期的行情数据。我们以最常用的日线数据daily()为例。

最基本的调用只需要股票代码和日期范围。A股股票的代码通常以.SZ(深交所)或.SH(上交所)结尾。

# 获取贵州茅台(600519.SH)2023年全年的日线数据 df = pro.daily(ts_code='600519.SH', start_date='20230101', end_date='20231231') print(df.head()) print(f"共获取到{len(df)}条数据")

执行这段代码,你会得到一个DataFrame,包含日期、开盘价、最高价、最低价、收盘价、成交量等关键字段。但直接这样调用可能会遇到两个常见问题:一是数据量很大时返回较慢,二是默认的字段顺序可能不符合你的分析习惯。

注意trade_date字段是整数格式(如20230101),在后续进行时间序列分析时,需要将其转换为标准的datetime类型。

一个更健壮、功能更丰富的调用示例应该包含错误处理和参数优化:

def fetch_stock_daily(ts_code, start_date, end_date, fields=None): """ 获取股票日线行情数据,并做基础处理。 参数: ts_code: 股票代码,如 '000001.SZ' start_date: 开始日期,格式 'YYYYMMDD' end_date: 结束日期,格式 'YYYYMMDD' fields: 指定返回的字段列表,为None则返回所有字段 返回: 处理后的 pandas DataFrame """ try: # 调用API df = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date, fields=fields) if df.empty: print(f"未获取到股票 {ts_code} 在指定时间段的数据。") return df # 1. 将交易日期转换为datetime类型,并设为索引 df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d') df.set_index('trade_date', inplace=True) df.sort_index(inplace=True) # 按时间升序排列 # 2. 重命名列,使其更易读(可选) column_mapping = { 'open': '开盘价', 'high': '最高价', 'low': '最低价', 'close': '收盘价', 'pre_close': '前收盘价', 'change': '涨跌额', 'pct_chg': '涨跌幅(%)', 'vol': '成交量(手)', 'amount': '成交额(千元)' } df.rename(columns=column_mapping, inplace=True) return df except Exception as e: print(f"获取数据时发生错误: {e}") return pd.DataFrame() # 返回空DataFrame # 使用函数获取中国平安(601318.SH)2024年第一季度数据 pingan_df = fetch_stock_daily('601318.SH', '20240101', '20240331') if not pingan_df.empty: print(pingan_df[['开盘价', '收盘价', '成交量(手)']].head())

通过封装函数,我们不仅实现了代码复用,还内置了数据清洗和转换的步骤,让获取到的数据立刻处于“分析就绪”状态。fields参数非常有用,当你只需要其中几列数据时,指定它可以减少网络传输的数据量,提高效率。

3. 进阶技巧:批量获取与数据管理

分析单只股票只是起点,真正的力量在于对比和组合分析。这就需要我们批量获取多只股票的数据。直接写循环调用API虽然可行,但效率不高,且可能触发频率限制。更优雅的方式是利用Tushare支持的批量代码查询,以及合理的缓存策略。

批量获取同一时间段多只股票数据:daily()接口的ts_code参数支持批量传入,代码间用逗号分隔。

# 一次性获取三只股票的数据 stock_codes = '600519.SH,000858.SZ,300750.SZ' batch_df = pro.daily(ts_code=stock_codes, start_date='20240101', end_date='20240331') print(batch_df['ts_code'].unique()) # 查看包含哪些股票

返回的DataFrame会包含所有股票的数据,并通过ts_code列区分。为了方便后续分析,我们经常需要将数据转换为面板数据格式,即每个股票单独成一个DataFrame,并以股票代码为键存储在字典中。

# 将批量获取的数据按股票代码拆分成字典 panel_data = {} for code in batch_df['ts_code'].unique(): panel_data[code] = batch_df[batch_df['ts_code'] == code].copy() # 对每个股票的数据进行同样的日期转换和索引设置 panel_data[code]['trade_date'] = pd.to_datetime(panel_data[code]['trade_date'], format='%Y%m%d') panel_data[code].set_index('trade_date', inplace=True) panel_data[code].sort_index(inplace=True) # 现在可以方便地访问任意一只股票的数据 if '600519.SH' in panel_data: maotai = panel_data['600519.SH'] print(maotai[['close', 'vol']].tail())

数据缓存与本地存储:反复从网络请求相同的数据是低效的。对于历史数据,一旦获取,就应该保存到本地。我常用的策略是使用pickleparquet格式存储DataFrame,因为它们能很好地保留数据类型和索引。

import os from pathlib import Path def save_stock_data(df, code, data_dir='./stock_data'): """将单只股票数据保存到本地""" Path(data_dir).mkdir(parents=True, exist_ok=True) file_path = Path(data_dir) / f"{code}.parquet" df.to_parquet(file_path) print(f"数据已保存至: {file_path}") def load_stock_data(code, data_dir='./stock_data'): """从本地加载单只股票数据""" file_path = Path(data_dir) / f"{code}.parquet" if file_path.exists(): df = pd.read_parquet(file_path) # 确保索引是datetime类型 if not isinstance(df.index, pd.DatetimeIndex): df.index = pd.to_datetime(df.index) return df else: print(f"本地文件 {file_path} 不存在。") return None # 使用示例:先尝试从本地加载,如果没有则从网络获取并保存 code = '000001.SZ' local_df = load_stock_data(code) if local_df is None: print("本地无缓存,从Tushare获取...") online_df = fetch_stock_daily(code, '20230101', '20231231') if not online_df.empty: save_stock_data(online_df, code) local_df = online_df

建立这样的本地数据仓库后,你的分析脚本启动速度会快很多,并且对Tushare API的依赖也降低了,在网络不稳定或API维护时仍能工作。

4. 基础数据分析与可视化实战

数据到手后,真正的乐趣才开始。我们使用pandas进行快速的数据洞察和特征计算。假设我们已经有了df这个包含某股票日线数据的DataFrame,且索引已是日期。

计算简单技术指标:移动平均线是最基础也最常用的指标之一。

# 计算5日、20日、60日简单移动平均线(SMA) df['SMA_5'] = df['收盘价'].rolling(window=5).mean() df['SMA_20'] = df['收盘价'].rolling(window=20).mean() df['SMA_60'] = df['收盘价'].rolling(window=60).mean() # 计算每日收益率 df['日收益率'] = df['收盘价'].pct_change() # 计算滚动波动率(以20日为例) df['波动率_20d'] = df['日收益率'].rolling(window=20).std() * (20**0.5) # 年化波动率粗略估算

制作一个简单的价格与均线图:可视化能让我们直观感受趋势。这里使用matplotlib

import matplotlib.pyplot as plt plt.style.use('seaborn-v0_8-darkgrid') # 使用一个好看的样式 fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True) # 子图1:价格与均线 ax1.plot(df.index, df['收盘价'], label='收盘价', linewidth=1.5, alpha=0.8) ax1.plot(df.index, df['SMA_20'], label='20日均线', linewidth=1.5, linestyle='--') ax1.plot(df.index, df['SMA_60'], label='60日均线', linewidth=1.5, linestyle=':') ax1.set_ylabel('价格 (元)') ax1.set_title('股票价格与移动平均线') ax1.legend() ax1.grid(True, alpha=0.3) # 子图2:成交量 ax2.bar(df.index, df['成交量(手)'], color='skyblue', alpha=0.7, label='成交量') ax2.set_xlabel('日期') ax2.set_ylabel('成交量 (手)') ax2.set_title('成交量') ax2.legend() ax2.grid(True, alpha=0.3) # 自动调整日期标签格式,避免重叠 fig.autofmt_xdate() plt.tight_layout() plt.show()

进行简单的数据汇总统计:pandasdescribe()函数可以快速给出分布概况,但我们也可以自定义更贴合金融场景的统计。

# 基础描述性统计 print(df[['收盘价', '日收益率', '成交量(手)']].describe()) # 计算一些关键金融统计量 annual_return = df['日收益率'].mean() * 252 # 粗略年化收益率 annual_volatility = df['日收益率'].std() * (252**0.5) # 年化波动率 sharpe_ratio = annual_return / annual_volatility if annual_volatility != 0 else None # 夏普比率(无风险利率假设为0) stats_summary = pd.DataFrame({ '统计量': ['年平均收益率', '年化波动率', '夏普比率(零无风险利率)'], '值': [f"{annual_return:.2%}", f"{annual_volatility:.2%}", f"{sharpe_ratio:.3f}" if sharpe_ratio else 'N/A'] }) print(stats_summary)

通过这几个步骤,你已经完成了从数据获取、清洗、存储到基础分析和可视化的完整闭环。这为你后续进行更复杂的策略回测、因子分析或机器学习建模打下了坚实的基础。记住,金融数据分析的核心在于提出正确的问题,并用数据去验证或探索答案,工具只是帮你更高效地完成这个过程。在接下来的探索中,你可能会对Tushare的其他数据接口(如财务数据、宏观经济数据)产生兴趣,也可以将pandas的威力发挥到更大,结合scikit-learnstatsmodels等库进行预测模型构建。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 20:34:08

别再搞混了!一文讲清加密狗、加密锁和普通U盘的区别

别再搞混了!一文讲清加密狗、加密锁和普通U盘的区别 你是不是也曾在网上搜索“软件授权工具”时,被一堆“加密狗”、“加密锁”、“硬件锁”之类的名词搞得晕头转向?或者,在需要保护一份重要文件时,看着手里那个长得和…

作者头像 李华
网站建设 2026/8/27 20:43:26

LingBot-Depth深度估计模型5分钟快速上手:从单张图片到3D点云

LingBot-Depth深度估计模型5分钟快速上手:从单张图片到3D点云 你是不是对3D世界充满好奇,想看看一张普通的照片背后藏着怎样的立体空间?或者你正在做机器人、AR/VR项目,需要从2D图像里“猜”出深度信息,但觉得传统方法…

作者头像 李华
网站建设 2026/8/27 20:49:34

Janus-Pro-7B效果对比:传统单路径vs Janus双路径架构精度提升

Janus-Pro-7B效果对比:传统单路径vs Janus双路径架构精度提升 1. 引言:多模态模型的架构演进挑战 在人工智能快速发展的今天,多模态模型已经成为技术领域的热点。传统的多模态模型通常采用单一路径架构,试图用同一套参数同时处理…

作者头像 李华
网站建设 2026/8/28 11:30:14

提示工程架构师的跨领域合作神器:5个好用的工具

提示工程架构师的跨领域合作神器:5个工具让协作效率翻倍 一、引言:跨领域合作的“痛”,你懂吗? 作为一名提示工程架构师,你是否经常遇到这样的场景: 产品经理拿着需求文档说“要生成‘有温度’的文案”&…

作者头像 李华
网站建设 2026/9/1 2:07:55

实战解析:如何构建高性能Chatbot Widget与Infinite Canvas交互系统

最近在做一个挺有意思的项目,需要把一个功能丰富的聊天机器人(Chatbot Widget)嵌入到一个可以无限缩放和平移的画布(Infinite Canvas)里。听起来很酷对吧?但做起来才发现,这里面的坑是真不少。传…

作者头像 李华