从Tushare到Excel:一份给量化新手的股票数据获取与清洗实战指南
2026/4/19 12:06:13 网站建设 项目流程

从Tushare到Excel:量化新手的股票数据处理全流程实战

刚接触量化分析时,最令人头疼的往往不是复杂的算法,而是如何把原始数据变成能直接使用的格式。记得我第一次从Tushare获取股票数据时,面对密密麻麻的字段和混乱的日期格式完全无从下手——数据有了,但根本没法用。这篇文章就是为解决这个问题而生,我会带你完整走一遍从数据获取到清洗再到Excel导出的全流程,重点解决那些没人告诉你的"脏活累活"。

1. 环境准备与数据获取

工欲善其事,必先利其器。在开始前,我们需要准备好Python环境和必要的库。推荐使用Anaconda创建专属的量化分析环境:

conda create -n quant python=3.8 conda activate quant pip install tushare pandas openpyxl

Tushare的接口分为免费版和Pro版,对于初学者,免费版已经能满足大部分需求。注册后获取token的方式很简单:

import tushare as ts ts.set_token('你的token') # 在Tushare官网注册后获取 pro = ts.pro_api()

获取单只股票历史数据的典型代码如下,以贵州茅台(600519.SH)为例:

df = pro.daily(ts_code='600519.SH', start_date='20200101', end_date='20221231')

初次拿到数据时,你可能会看到这样的结构:

ts_codetrade_dateopenhighlowclosepre_closechangepct_chgvolamount
600519.SH202212301720.01728.01705.01719.01720.18-1.18-0.0733826.61580902.3

提示:Tushare返回的DataFrame默认索引是无序数字,trade_date是字符串格式,这些都需要后续处理

2. 数据清洗的核心操作

原始数据往往存在各种问题:日期格式混乱、缺失值、需要计算的衍生字段等。下面这些操作能帮你把"生数据"变成"熟数据"。

2.1 基础清洗步骤

首先处理最棘手的日期问题。Tushare返回的trade_date是YYYYMMDD格式的字符串,我们需要转换为Pandas的datetime类型:

df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d') df = df.sort_values('trade_date') # 按日期排序

接下来处理字段命名。Tushare的原始字段名对中国人不够友好,我们可以建立映射关系进行重命名:

column_mapping = { 'ts_code': '股票代码', 'trade_date': '交易日期', 'open': '开盘价', 'high': '最高价', 'low': '最低价', 'close': '收盘价', 'pre_close': '前收盘价', 'change': '涨跌额', 'pct_chg': '涨跌幅(%)', 'vol': '成交量(手)', 'amount': '成交额(千元)' } df = df.rename(columns=column_mapping)

2.2 处理缺失值与异常值

股票数据中常见的缺失情况包括:

  • 停牌日数据全为NaN
  • 极端行情导致的部分字段缺失
  • 早期数据某些字段可能不存在

处理方案需要根据具体情况选择:

# 简单删除缺失值(适用于停牌日) df = df.dropna(subset=['开盘价','收盘价']) # 向前填充(适用于部分字段缺失) df['成交量(手)'] = df['成交量(手)'].fillna(method='ffill') # 设置默认值(适用于早期数据) df['成交额(千元)'] = df['成交额(千元)'].fillna(0)

2.3 计算衍生指标

原始数据只提供基础字段,实际分析中我们还需要计算各种技术指标。以下是几个常用指标的计算方法:

# 计算5日均线 df['5日均线'] = df['收盘价'].rolling(5).mean() # 计算每日振幅(最高价-最低价)/前收盘价 df['日振幅(%)'] = (df['最高价'] - df['最低价']) / df['前收盘价'] * 100 # 计算换手率(需要知道总股本) total_share = 12.56 # 贵州茅台总股本(亿股) df['换手率(%)'] = df['成交量(手)'] * 100 / (total_share * 1e4)

3. 数据可视化与探索性分析

清洗后的数据需要先进行可视化检查,确保数据质量。Pandas内置的plot方法能快速生成基础图表:

import matplotlib.pyplot as plt # 设置中文显示 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 绘制价格走势图 df.set_index('交易日期')[['收盘价','5日均线']].plot( figsize=(12,6), title='贵州茅台2020-2022年股价走势', grid=True ) plt.ylabel('价格(元)') plt.show()

常见的探索性分析还包括:

  • 收益率分布检查
  • 成交量与价格变动关系
  • 波动率聚类现象观察
  • 周内效应分析

4. 导出为结构化Excel文件

最终我们需要把处理好的数据导出为Excel,方便与他人协作或存档。Pandas的Excel导出功能非常强大,可以支持多sheet、格式设置等高级功能。

4.1 基础导出方法

最简单的单sheet导出:

df.to_excel('茅台股票数据.xlsx', index=False)

但实际项目中,我们通常需要更结构化的输出:

with pd.ExcelWriter('茅台股票分析报告.xlsx') as writer: # Sheet1:原始数据 df.to_excel(writer, sheet_name='日线数据', index=False) # Sheet2:月度统计 monthly = df.resample('M', on='交易日期').agg({ '开盘价':'first', '最高价':'max', '最低价':'min', '收盘价':'last', '成交量(手)':'sum' }) monthly.to_excel(writer, sheet_name='月度统计') # Sheet3:技术指标 tech_indicator = df[['交易日期','5日均线','日振幅(%)','换手率(%)']] tech_indicator.to_excel(writer, sheet_name='技术指标', index=False)

4.2 添加Excel格式

使用openpyxl引擎可以进一步美化输出:

with pd.ExcelWriter('茅台股票分析报告(美化版).xlsx', engine='openpyxl') as writer: df.to_excel(writer, sheet_name='日线数据', index=False) # 获取workbook和worksheet对象 workbook = writer.book worksheet = writer.sheets['日线数据'] # 设置列宽 for col in worksheet.columns: max_length = max(len(str(cell.value)) for cell in col) worksheet.column_dimensions[col[0].column_letter].width = max_length + 2 # 添加条件格式 from openpyxl.formatting.rule import ColorScaleRule rule = ColorScaleRule(start_type='percentile', start_value=10, start_color='FF0000', mid_type='percentile', mid_value=50, mid_color='FFFFFF', end_type='percentile', end_value=90, end_color='00FF00') worksheet.conditional_formatting.add('H2:H1000', rule)

5. 实战中的常见问题与解决方案

在实际操作中,你可能会遇到以下典型问题:

问题1:数据量太大导致内存不足

  • 解决方案:分时间段获取数据
date_ranges = [('2020-01-01','2020-06-30'), ('2020-07-01','2020-12-31')] dfs = [] for start, end in date_ranges: dfs.append(pro.daily(ts_code='600519.SH', start_date=start.replace('-',''), end_date=end.replace('-',''))) df = pd.concat(dfs)

问题2:需要处理多只股票数据

  • 解决方案:构建股票池批量处理
stock_list = ['600519.SH', '000858.SZ', '600036.SH'] all_data = {} for code in stock_list: try: all_data[code] = pro.daily(ts_code=code, start_date='20200101', end_date='20221231') print(f"{code} 数据获取成功") except Exception as e: print(f"{code} 获取失败: {str(e)}")

问题3:自动更新数据

  • 解决方案:记录最后更新时间,增量获取
import os if os.path.exists('last_update.txt'): with open('last_update.txt','r') as f: last_date = f.read() else: last_date = '20200101' new_data = pro.daily(ts_code='600519.SH', start_date=last_date, end_date=pd.Timestamp.now().strftime('%Y%m%d')) # 更新记录文件 with open('last_update.txt','w') as f: f.write(pd.Timestamp.now().strftime('%Y%m%d'))

处理股票数据最考验的不是编程技巧,而是对金融业务的理解。比如复权处理就有多种方式:

  • 前复权:以当前价格为基准调整历史数据
  • 后复权:以历史价格为基准调整当前数据
  • 定点复权:以特定时点为基准
# 简单的前复权处理示例 df['复权因子'] = df['收盘价'] / df['前收盘价'].cumprod() df['前复权收盘价'] = df['收盘价'] / df['复权因子']

记住,好的数据清洗流程应该是可复现的。建议把所有处理步骤封装成函数甚至类,方便下次直接调用:

class StockDataProcessor: def __init__(self, token): ts.set_token(token) self.pro = ts.pro_api() def get_clean_data(self, ts_code, start_date, end_date): raw = self.pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date) # 这里放入之前所有的清洗步骤 return cleaned_data

最后分享一个实际项目中的经验:处理金融数据时,一定要保留原始数据的备份,所有转换步骤都应该记录在代码中而非手动操作。我曾因为直接修改原始数据而丢失了重要信息,不得不重新获取,浪费了大量时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询