十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用WindPy获取沪深300日行情并写入Excel的完整教程

用WindPy获取沪深300日行情并写入Excel的完整教程 简介本资源是一份面向金融数据开发初学者与Python量化入门者的实用脚本聚焦解决“如何从专业金融数据库高效获取并落地沪深300日频行情”这一典型需求。代码基于Wind Py APIwindpy库调用WSD接口完整实现指数代码000300.XSHG的收盘价、成交量等字段拉取并通过pandas结构化处理后导出为Excel文件适用于投资分析、策略回测数据准备及自动化日报生成等场景。压缩包仅含1个核心Python脚本.py体积精简至2KB无冗余依赖或配置文件开箱即用便于快速理解Wind数据接入逻辑与Excel导出全流程。目前已有1937人学习下载读者可直接复用该脚本作为数据获取模块嵌入自身分析体系亦可据此延伸学习Wind其他指标调用、异常连接处理及批量指数数据采集等进阶技能。 直接说结论这类把Wind数据落到Excel的活儿在数据开发里属于最基础但最高频的一类需求。我见过太多人要么每天手动从Wind客户端导数据要么写一次性脚本用完就删真正做成一个能复用、能交给别人跑的流程反而没几个人仔细琢磨过。这篇文章就用“获取沪深300日行情并存入Excel”这个最典型的场景把整个从Wind到Excel的链路拆开讲透。先说清楚这文章能帮你解决什么。如果你在券商、基金、银行或者任何跟金融数据打交道的岗位大概率会遇到这种需求领导或者业务同事某天丢过来一句话“帮我拉一下沪深300今年以来的日行情导出Excel给我”。听起来简单但真做起来会发现一堆问题——Wind的数据怎么用Python取取出来是什么结构日期和涨跌幅怎么处理Excel里中文列名怎么弄跑完这次下次换个股票池怎么办这篇文章就是围绕这些真实痛点展开的适合刚接触Wind接口的Python新手也适合做数据开发但没系统处理过Wind数据的朋友已经写过类似脚本的人也能在里面找到一些性能优化和坑位排查的思路。1. 项目整体设计与方案选型1.1 需求拆解看似只要一行代码实际要解决四件事先别急着写代码把需求拆开看。“获取沪深300日行情数据并存入Excel文件”这句话背后至少藏着四个子问题数据从哪来Wind金融终端通过WindPy接口也就是wind的python接口包获取这是国内金融行业最主流的数据源之一。取什么数据日行情但日行情具体包含哪些字段通常至少包括开高低收、成交量、成交额、涨跌幅有时候还要复权因子、换手率、振幅。数据存哪Excel文件但Excel有旧版.xls和新版.xlsx的区别Excel单sheet最多104万行xlsx沪深300日线一年才240条左右怎么存都够但要考虑列名是否可读、格式是否友好。流程怎么跑是一次性脚本还是可复用的函数是不是要支持换代码、换日期范围、换字段很多人第一步就栽在“直接调接口”上。WindPy不是pip install就能用的普通库它依赖你本地装了Wind金融终端而且接口启动时要和终端握手。这个特性直接决定了整个项目的方案设计——你不可能在没有Wind客户端的机器上跑通这个脚本所以部署环境是有硬约束的。1.2 选型对比为什么用WindPy加pandas加Excel而非其他组合市面上能拿沪深300行情的途径其实不少我列个表对比一下方案是否免费数据质量实时性Python接入难度适用场景WindPyWind终端否需付费终端高金融行业常用强含实时/历史中等依赖本地终端机构内日常工作Tushare Pro积分制部分免费较高适合研究有延迟低requests即可个人研究、学习AkShare免费中等源不稳定有延迟低pip安装快速验证、教学东方财富/新浪接口免费中等有延迟低需爬虫非正式场景如果你在公司做正式的数据交付选Wind基本是没得选的——业务方手上的底稿、复核口径、多数据源交叉验证都是基于Wind来的。Tushare和AkShare的数据口径和Wind存在差异到时候对不上数背锅的是你。工具链上我用的是pandas加openpyxl的组合。pandas的to_excel方法底层就是调openpyxl或者xlsxwriter写入Excel而openpyxl的优势在于后续还能对Excel做格式化调整。为什么不直接用csv因为业务同事就认Excelcsv打开中文乱码、列宽难受、没有sheet概念交付体验差一大截。1.3 项目目标画像一份能够直接交付的数据资产做数据开发的人最容易犯的一个毛病是只关注“代码跑通了”而忽略“交付物是否好用”。一个合格的沪深300日行情Excel文件至少应该是这样的第一行列名清晰比如“日期”“开盘价(元)”“收盘价(元)”“涨跌幅(%)”等中文名称而不是open、close这种英文。日期格式是标准YYYY-MM-DD不是pandas默认的时间戳。数据按日期升序排列。涨跌幅是百分比数值保留两位小数。文件打开后不报“格式与扩展名不一致”的警告。这些细节看起来琐碎但正是经验活。很多人代码写完了文件也生成出来了结果业务方打开一眼就觉得“不太专业”问题往往就出在这些格式化细节上。2. 核心原理与WindPy接口玩法2.1 WindPy到底是什么它和Wind终端是什么关系WindPy是万得提供的一个Python接口包本质上是Wind终端的一个客户端代理。你在Python里调用w.wsd()它其实是通过本地进程通信把请求发给正在运行的Wind终端终端拿到数据后再通过这个通道返回给Python。这意味着三件事使用前必须启动Wind金融终端并且保持登录状态。Wind终端的版本和WindPy的版本必须匹配不然会握手失败。每次调用接口本质上是跟终端的一次交互频繁调用会有性能损耗。理解了这个机制你就能想到很多坑。比如很多人报了errorCode-1第一反应是代码错了但实际上往往是Wind终端没启动、没登录、或者证书过期了。再比如有人写了个循环一条条拉历史数据发现速度慢到怀疑人生那是因为每次都走了一遍完整的本地通信链路。2.2 三个核心接口wsd、wss、wsq分别什么时候用WindPy里有三个最常见的接口很多新手容易混w.wsd()获取历史日/周/月序列数据比如“沪深300过去一年的日K线”这是本项目用的核心接口。w.wss()获取截面数据比如“某一天沪深300所有成分股的市盈率”返回的是同一时点的多标的数据。w.wsq()获取实时行情比如“当前沪深300最新价”通常是盘中做监控用的。三者本质区别是wsd是时间序列视角wss是截面视角wsq是即时快照视角。本项目是拿历史日线所以用wsd。但等你想批量拉300只成分股的时候wss会派上用场——先用它的indexcons字段把成分股列表一次性拉出来。另外wsd支持一个相当强但也容易忽略的参数returnType。比如要前复权数据可以在参数字符串里加PriceAdjF要后复权PriceAdjB不复权默认是None。沪深300指数本身是价格指数严格来说不存在个股除权的问题但如果后续你把股票池扩展成成分股这个参数就要特别注意——历史价格不复权和前复权之间能差出好几块钱直接影响收益率计算。2.3 Wind返回的数据结构pandas怎么接得住大家第一次接触WindPy时最容易懵的是它的返回结构。它在Python侧的封装是基于tuple的和普通requests返回json完全是两码事。看代码from WindPy import w import pandas as pd w.start() error_code, data, times w.wsd( 000300.SH, open,high,low,close,volume,amount, 2024-01-01, 2024-12-31, )这里返回的是一个长度为3的元组部分版本是2个或4个别慌以你本地的版本为准。通常第一个是errorCode等于0表示成功第二个是数据数组按请求字段顺序排列每个字段是一个listlist里每个元素对应一天的数值第三个是时间序列list里面是Wind的datetime.datetime对象。所以pandas接数据时最直接的方式是df pd.DataFrame({ 日期: times, 开盘价: data[0], 最高价: data[1], 最低价: data[2], 收盘价: data[3], 成交量: data[4], 成交额: data[5], })但如果字段有十几个手动写映射容易出错。我一般会先构造一个字段和中文名的映射表再做批量columns赋值field_map { open: 开盘价(元), high: 最高价(元), low: 最低价(元), close: 收盘价(元), volume: 成交量(股), amount: 成交额(元), pct_chg: 涨跌幅(%), } fields list(field_map.keys()) _, data, times w.wsd(000300.SH, ,.join(fields), start_date, end_date, ) df pd.DataFrame(data, indextimes).T df.index.name 日期 df.columns [field_map[col] for col in fields] df.reset_index(inplaceTrue)这里我把data转置了一下因为Wind返回的data是按字段分列的pandas接收后希望每列是一个字段直接pd.DataFrame(data)的话字段会变成行而不是列做一次.T再指定columns就够了。这个细节不算高级但新手在这一步卡住的情况非常普遍。3. 完整实操从零到一写出可复用的Excel落盘脚本3.1 环境准备安装WindPy、处理版本匹配问题第一步是安装WindPy。官方渠道通常是两个一个是在Wind终端里找到“插件中心”直接安装Python接口另一个是用pip从万得提供的源安装。现在Wind提供了pip install WindPy的渠道但版本兼容性依然以本地终端为准。实际操作中我的建议是不要自己凭感觉装而是在Wind终端里打开“帮助”里的“Python接口”或者插件中心看它推荐的安装方式。因为WindPy和终端之间是强绑定关系你pip装了个跟终端不匹配的版本运行时大概率报errorCode-4或者直接连不上。装完之后先跑一段最小验证代码from WindPy import w w.start() print(w.isconnected())如果输出True说明握手成功。如果输出False检查三件事Wind终端是否启动、是否已登录、终端版本是否过旧需要更新。3.2 核心代码获取沪深300日行情并写入Excel下面这段是我在实际项目中用的一套相对完整的实现包含取数、格式化、落盘三步from WindPy import w import pandas as pd from datetime import datetime FIELD_MAP { open: 开盘价(元), high: 最高价(元), low: 最低价(元), close: 收盘价(元), volume: 成交量(股), amount: 成交额(元), pct_chg: 涨跌幅(%), } def fetch_hs300_daily(start_date: str, end_date: str) - pd.DataFrame: 获取沪深300日行情数据返回DataFrame if not w.isconnected(): w.start() fields list(FIELD_MAP.keys()) err_code, data, times w.wsd( 000300.SH, ,.join(fields), start_date, end_date, FillPrevious ) if err_code ! 0: raise RuntimeError(fWind接口调用失败errorCode{err_code}) df pd.DataFrame(data, indextimes).T df.index.name 日期 df.columns [FIELD_MAP[col] for col in fields] df.reset_index(inplaceTrue) return df def save_to_excel(df: pd.DataFrame, file_path: str): DataFrame写入Excel并做基础的格式优化 with pd.ExcelWriter(file_path, engineopenpyxl) as writer: df.to_excel(writer, sheet_name沪深300日行情, indexFalse) # 调整列宽与格式 ws writer.sheets[沪深300日行情] ws.column_dimensions[A].width 14 for col_idx in range(2, len(df.columns) 2): ws.column_dimensions[chr(64 col_idx)].width 16 # 冻结首行 ws.freeze_panes A2 if __name__ __main__: start 2024-01-01 end datetime.now().strftime(%Y-%m-%d) daily_df fetch_hs300_daily(start, end) save_to_excel(daily_df, 沪深300日行情.xlsx) print(f数据获取完成共{len(daily_df)}条记录已保存至Excel)这段代码你应该能直接跑通。我解释几个比较关键的细节FillPrevious这个参数指的是如果某一天停牌或者无交易数据用前一条交易日数据填充。沪深300是指数不存在这个情况但如果后面扩展成分股数据就会用到提前写上不影响什么。w.start()如果之前已经启动过重复调用不会有副作用所以我每次都检查一下连接状态再启动。chr(64 col_idx)这种列宽写法只适用于列数少于26列的情况。如果你字段扩展到30个以上建议用openpyxl.utils里的get_column_letter比如from openpyxl.utils import get_column_letter然后ws.column_dimensions[get_column_letter(col_idx)].width 16。3.3 让Excel更好看列宽、日期格式、数字格式、冻结窗格上面那段代码已经写了列宽和冻结首行但作为一个交付给业务的Excel文件还可以更进一步。先说日期列。pandas的to_excel默认会把datetime格式写成一个看起来像时间戳的东西比如2024-01-04 00:00:00这非常不专业。可以在写入Excel前把日期列转成字符串df[日期] df[日期].dt.strftime(%Y-%m-%d)同样的道理涨跌幅、成交额这些数值列可以设置Excel的数字格式。不过openpyxl的number_format设置需要定位到单元格或者整列操作起来稍微繁琐一点。我通常会在save_to_excel里加一步from openpyxl.styles import Alignment ws writer.sheets[沪深300日行情] # 设置数字列格式保留两位小数使用千分位分隔符 for col_idx in range(2, len(df.columns) 2): col_letter get_column_letter(col_idx) for row_idx in range(2, ws.max_row 1): cell ws[f{col_letter}{row_idx}] cell.number_format #,##0.00 # 让所有单元格内容垂直居中 for row in ws.iter_rows(): for cell in row: cell.alignment Alignment(verticalcenter)这里有个性能隐患要注意如果数据量上万行逐单元格设置格式会很慢。沪深300日线一年才200多条完全无所谓但你心里得有个数将来换成分钟级数据或者几千只股票的时候这种做法不可取那就要考虑用xlsxwriter引擎或者在写之前就用DataFrame的style来处理。另外还可以顺手设置一个“行情日期”说明行比如在文件顶部右侧加一行“数据截至日期2024-12-31”方便业务方快速判断数据新鲜度。不过这种方式会让表头结构变复杂如果不是硬性需求我通常不加避免让代码维护变得麻烦。3.4 多标的场景获取成分股并批量循环下载沪深300单指数只解决了一个问题但真实业务中更常见的是把沪深300的300只成分股的历史行情都拉下来然后放进同一个Excel的不同sheet或者打包成一个zip。这里涉及两个关键步骤。第一步获取成分股列表。用w.wss配合indexcons参数from WindPy import w w.start() err_code, data, times w.wss( 000300.SH, indexcons, , tradeDate20241231 ) # data是一个二维数组第一行就是成分股代码列表 stocks data[0] print(len(stocks)) # 通常是300 print(stocks[:5]) # 前5只股票代码有了股票池第二步就是循环调用w.wsd获取每只股票的日线数据。这里最大的坑是性能。300只股票循环下来如果每只都走一次完整接口调用快的也要5到10分钟慢的可能要20分钟以上。优化思路有几种减少调用次数wsd其实支持多标的并行请求比如你可以把300个代码以逗号拼成一个大字符串传进去一次调用返回所有数据只是数据结构会变成多维数组处理时稍麻烦。Wind对单次请求的证券数量有限制实际操作中我会分成20到30只一批来调用兼顾速度和稳定性。本地缓存把已下载的数据缓存到本地pkl或csv下次跑的时候如果日期范围有重叠直接读取本地数据再增量更新避免重复拉全量。日志输出循环里加个进度打印比如每50只打印一次当前进度防止长时间无响应让人以为脚本卡死了。我实际项目里一般这么写循环batch_size 30 results {} for i in range(0, len(stocks), batch_size): batch stocks[i:ibatch_size] code_str ,.join(batch) err_code, data, times w.wsd( code_str, close, start_date, end_date, ) # 多标的数据结构data[0]是第一个字段里面按标的分层 # 具体维度因版本而异需要先打印检查再处理 print(f已处理 {min(ibatch_size, len(stocks))}/{len(stocks)})特别提醒一下多标的wsd返回的数据结构和你单独取一个标的时不一样它是多维嵌套的不同版本WindPy的组织方式也不尽相同所以拿到数据后第一件事是print(data)观察结构不要想当然地套用单标的的解析代码。这个坑我踩过写代码时务必验证。4. 常见问题与排查技巧实录4.1 errorCode的含义和解决方案WindPy返回的errorCode是排查问题的第一入口。我从实际项目中总结了几个高频值errorCode含义解决方案0成功无需处理-1连接失败或未登录检查Wind终端是否启动、是否登录、证书是否过期-4参数错误检查代码、日期格式、字段名称是否合法-40520001无权限或未订阅该数据检查Wind账号是否有相应数据权限-40520003数据不存在检查指数代码是否正确、日期范围是否有效日期范围这个坑值得展开一下很多新手会把起始日期写成2024-01-01就以为有数据但如果那天恰好是元旦休市Wind返回的数据里就不会有这个日期。更常见的情况是把起始日期写成了周六这时候应该用ED等日历参数或者直接用交易日历接口w.tdays()去获取交易日序列再对齐。4.2 Excel写入失败的几类问题写入Excel最常见的报错有两个一个是文件被占用导致PermissionError另一个是pandas版本和openpyxl版本不兼容导致的异常。文件被占用的问题很好识别报错信息里会出现Permission denied或者[Errno 13]。解决办法就是让业务方关掉已经打开的Excel文件或者在代码里预判一下import os output_path 沪深300日行情.xlsx if os.path.exists(output_path): try: os.remove(output_path) except PermissionError: raise SystemExit(Excel文件已被打开请先关闭后再运行脚本)版本兼容性问题的典型特征是报ModuleNotFoundError: No module named openpyxl或者ExcelWriter里面某个方法找不到。处理方法是对齐pandas和openpyxl的版本通常pip install --upgrade pandas openpyxl就能解决。实在不想动全局环境就建个虚拟环境专门跑这个脚本。还有一个很少被人提到但实际经常出现的情况to_excel写的时候sheet名用了中文如果加上一些特殊字符比如/:?*[]Excel会直接报错。这个很少有人遇到但我见过有人把sheet命名为沪深300(2024/01-12)然后死活写不进去其实就是斜杠引发的。4.3 数值精度和数据口径问题金融数据最怕对不上口径。沪深300指数取回来的是点位但如果你用相同接口取个股的收盘价是否复权、复权方式是前复权还是后复权都会影响数值。同一个股票不复权价格可能是50元前复权价格可能是38元后复权价格可能是460元数字各异但都“没错”。怎么避免建议在Excel文件里加一个说明sheet或者在列名上直接标注比如列名叫“收盘价(前复权)”而不是简单写“收盘价”。这么做的好处是后期追溯成本低得多否则三个月后你翻回这个文件根本记不清当时是怎么取的数。再有一个精确度问题Wind返回的数值是floatpandas处理时有时候会出现浮点误差比如显示成3564.1500000000003。处理方式是在to_excel之前对数值列做roundnumeric_cols df.select_dtypes(include[float64]).columns df[numeric_cols] df[numeric_cols].round(4)实际金融行情最多保留到小数点后四位就够了指数点位保留两位也够用。4.4 跑批脚本的稳定性设计如果这个脚本要放到调度系统里每天自动跑稳定性就是第一优先级。我总结三条经验每次运行都把日志写到文件不只是print到控制台。调度系统往往不会保留stdout出了问题连日志都没得查。对Wind连接做重试机制。Wind终端偶尔会抽风重启后连接就恢复了代码里可以封装一个retry装饰器或者简单用循环重试三到五次。失败时输出详细错误信息包括当前获取的标的、日期、错误码方便定位是单一标的问题还是整体故障。def retry_call(func, *args, retries3, **kwargs): for attempt in range(retries): err_code, data, times func(*args, **kwargs) if err_code 0: return data, times time.sleep(2) raise RuntimeError(f重试{retries}次仍失败errorCode{err_code})5. 扩展思路这个项目还能往哪些方向走5.1 从单文件到数据仓库的演进很多数据开发的人接触这个需求的时候都会觉得“就存个Excel嘛有什么好做的”但换个角度看这其实是数据接入的第一步。Excel只是交付形式背后的数据可以轻松写到数据库里比如接一个to_sqlfrom sqlalchemy import create_engine engine create_engine(mysqlpymysql://user:passwordhost:3306/db) df.to_sql(hs300_daily, engine, if_existsreplace, indexFalse)这样Excel给业务看数据库给下游系统用一套取数逻辑两个出口很直观。5.2 从日线到分钟线、从单市场到多市场Wind的wsd本身就支持分钟的周期参数把period1m加进去就能拿分钟数据。沪深300只是A股的代表指数如果你把代码换成港股、美股指数原理完全一样需要注意的只是时区还有交易日的定义差异。每次改变标的大类都要重新检查“数据口径”和“交易日历”这是拓展过程中最容易翻车的点。5.3 从手动触发到定时调度脚本写完之后可以把它接进crontab或者调度平台每天收盘后自动拉取当日数据自动生成Excel发邮件给业务方。这一步能给业务同事带来极大的便利也让你在团队里的“口碑”上升不少。不过这种自动化一定要搭配前面说的日志和重试机制否则某天Wind登录态过期脚本静静失败一个月没人发现那才是真正的灾难。结尾一点个人体会做了一段时间数据开发之后我越来越觉得手里的工具本身不是核心核心是能不能稳定、可靠、可追溯地把数据交付出去。WindPy加pandas加Excel这个组合技术门槛其实不高真正的价值在于你对数据口径的理解、对接口行为的熟悉、对交付细节的把控。第一次写这个脚本的时候我也被wind返回的数据结构搞得一头雾水也被Excel中文列名和日期格式折磨过。但把这些坑一个个填平之后你就会发现这类需求已经难不倒你了而且你会很自然地想把它扩展到更多标的、更多字段、更多下游场景。希望这篇文章能帮你少走一些弯路哪怕只有一条经验让你少查半天文档也挺值的。本文还有配套的精品资源点击获取
返回列表