
简介面向金融数据开发者与Python爱好者资源核心是解析同花顺特有的.day二进制格式仅需20行代码即可提取股票历史日线中的日期、开盘价、收盘价、最高价、最低价、成交量与成交额等关键字段适合初学二进制文件处理或需要快速接入行情数据的用户。压缩包为7z格式共2个文件包含股票600000的日线样本600000.day及对应解析脚本getAllData.py包体仅4KB轻量易用无需安装第三方库即可运行。已有2237人学习示例脚本采用open()二进制读取、struct字段解包与日期转换完整呈现字节记录到结构化数据的还原过程并可直接输出便于进一步分析的列表或DataFrame结构同时也为批量处理多只股票、异常数据容错等扩展需求提供了清晰起点。 前阵子有个量化回测的需求要把同花顺本地缓存的日线数据导出来加工成自己的数据仓库。翻了一圈文档发现同花顺并没有公开的官方文件格式说明网上能找到的资料也大多是零散的只言片语。于是只能对着二进制文件一点点抠结构中间踩了不少坑也把日线文件、分钟线、板块文件这些小众格式摸了一遍。这篇文章就把整个解析过程完整写出来给后续有同样需求的读者一个可参考的路线。1. 先解决三个问题文件在哪、文件叫什么、里面存的是什么解析任何私有格式的第一步永远是先找到文件本身。同花顺的历史数据并不是存在一个统一的数据库中而是按市场、按周期拆散成大量小文件存放在安装目录的特定子目录下。1.1 默认安装路径下的数据中心以 Windows 版为例同花顺默认安装路径通常是C:\new_jyq\或C:\hexin\具体看你安装时的选择。历史日线数据一般位于[安装目录]\history\day\sh\ [安装目录]\history\day\sz\ [安装目录]\history\day\bj\sh目录存放上海证券交易所的股票日线数据sz目录存放深圳证券交易所的股票日线数据bj目录是北交所的数据版本较新的客户端才会有。每个目录下面都是一个个以证券代码命名的文件。比如sh600000.day就是浦发银行在上海市场的日线文件sz000001.day是平安银行在深圳市场的日线文件。如果同花顺还没有下载过对应股票的历史数据那么在相应目录里是找不到这个文件的需要先在客户端里把日线数据下载完整。注意不同版本的同花顺历史数据目录可能不同。有的版本放在[安装目录]\T0002\hq_cache\下面有的版本甚至把数据按年月拆成子目录。最稳妥的办法是在安装目录里搜*.day后缀文件先确认实际位置再去写解析脚本。1.2 从文件名反推证券代码文件名的规则相对固定前缀两个字母是市场标识后面6位数字是证券代码。这个规则在解析时特别有用因为你不需要打开文件内容就能知道这只股票属于哪个市场方便批量导入时自动归类。不过要小心一点同花顺的板块指数、自定义指数也可能出现在历史数据目录中代码可能出现字母加数字的组合指数文件也可能以sh000001.day这样的形式存在。所以解析完之后建议对照一下证券代码表把指数、基金、债券等非个股数据单独分类处理。2. 日线记录的核心二进制布局搞清楚文件位置之后真正的重头戏来了二进制结构。这也是整个解析过程中最需要耐心的地方。我用十六进制编辑器打开一个.day文件从文件头开始逐个字节看最终把结构整理了出来。2.1 文件头先用魔数确认格式同花顺日线文件的头部通常以 4 个字节的魔数开头比较常见的是5A 24 0A 02对应的十进制是1512595970。魔数的作用是让程序快速判断文件是不是预期的格式解析时如果不匹配可以直接报错避免后面解析出一堆乱码。魔数之后通常是文件版本号和一些保留字段但这里要特别提醒不同版本的同花顺客户端写出的文件头存在差异。我实测过几个版本文件头长度不是完全一样的。有的版本在魔数后还有 16 字节的附加信息有的版本则没有。所以解析时不要对文件头之后的偏移量做死假设建议先用hexdump看一下实际文件确认数据记录是从哪个偏移开始的。通常来说偏移 32 字节之后就是连续的数据记录体。2.2 32字节数据记录逐字段拆解同花顺日线文件的单条数据记录长度是 32 字节按小端序存储。具体结构如下相对偏移类型字段说明0int32日期从 1990-12-19 起算的天数4int32开盘价实际价格乘以 1008int32最高价实际价格乘以 10012int32最低价实际价格乘以 10016int32收盘价实际价格乘以 10020float成交量单位通常为股24float成交额单位为元28int32保留字段部分版本存放涨跌幅或复权因子这个布局是我在自己的环境里反复验证过的大部分.day文件都适用。但不同客户端版本之间最后 4 字节的保留字段含义可能不太一样有的版本存的是当日的涨跌幅有的版本存的是空值或固定标记。读取的时候注意区分我一般先把这个字段单独打印出来看一遍分布确认它的实际含义再决定是否使用。识别布局时最实用的小技巧先用同一只股票最近几天的收盘价去对数据。你随便打开客户端记下一个已知日期和收盘价然后在二进制文件里搜索放大 100 倍后的整数值。比如某日收盘价 12.35你就搜十六进制的04 D2 00 00即 1234如果搜到了就知道这个字段存的是价格放大值。这个方法在解析所有未知行情文件格式时都有效。2.3 价格和成交量单位的缩放问题这是最容易踩坑的地方。同花顺日线文件里的 OHLC 四个价格字段都是整数实际价格要除以 100。比如字段值是1234对应股价就是12.34。如果不做这个换算后面算收益率、做技术指标会全部偏掉。成交量字段需要特别注意它存的是 float 类型单位是股数不是手数。如果你习惯用“手”作为成交量单位解析完之后还要再除以 100。成交额字段单位则是人民币元。我见过有人在解析完之后拿成交额除以成交量想算均价结果发现数量级差 100 倍就是没有注意到股和手的换算。3. 日期编码从“基准日偏移”算回真实交易日日线文件的日期字段不是标准 Unix 时间戳也不是常见的YYYYMMDD整数而是一个偏移量从 1990 年 12 月 19 日上交所开业日起算的天数。例如日期字段值 0对应 1990-12-19 日期字段值 1对应 1990-12-20 日期字段值 365对应 1991-12-191991 不是闰年正好 365 天换算成 Python 代码非常简单from datetime import date, timedelta def hexin_day_to_date(offset: int) - date: return date(1990, 12, 19) timedelta(daysoffset)这个基准日期的设定其实暗合了中国证券市场的历史。上交所 1990 年 12 月 19 日开业同花顺把这一天作为日线数据的起算点逻辑上说得通。理解了这个背景你就不会把基准日猜成 Unix 纪元1970-01-01了。需要注意日线数据文件里只有交易日数据周六、周日以及法定节假日都不会出现记录。所以解析出的日期列表天然就是离散的。在后续做回测时如果遇到“下一个交易日”的查询需求不能直接对日期加 1而要从解析出的日期序列里用searchsorted这类方法去查找或者事先建立一个交易日列表。另外考考你假设某文件里第一条记录日期字段是11069那对应的是哪一天用上面的公式算一下大约是 2021 年 4 月。你可以打开同花顺客户端看一下那天的行情验证解析是否正确。这个小验证能让你快速确认整个解析链路没有问题。4. 分钟线、板块文件与自选股顺带梳理周边格式日线文件解析完之后很多人会顺手想把分钟线、板块文件也一起解析了。这几个格式之间有关联但结构差异很大我在这里把各自的特点和踩坑情况简单记录一下。4.1 分钟线文件结构类似但日期字段含义不同分钟线文件的扩展名一般是.min或.m1、.m5之类字段布局跟日线文件类似也是 32 字节一条记录。最大的区别在于日期字段的含义分钟线文件里的“日期时间”不再是从基准日算起的天数而是按分钟偏移量或者一个压缩的日期时间整数来存储具体布局在不同版本间差异很大。我在解析 5 分钟线时发现部分版本的文件里日期字段存的是YYYYMMDDHHMM这样的 12 位整数也有版本把日期拆成两个 int16 分别存放日期和时间。没有统一标准所以建议按实际文件推断不要照搬日线的解析逻辑。4.2 板块文件用文本方式先侦察再动刀同花顺的板块文件通常位于安装目录的T0002\block_zs.dat、block_gn.dat等路径里面保存了板块定义、板块内股票列表和板块指数信息。这类文件既有二进制段也有明文文本段解析难度比日线文件大。一个实用的侦察技巧是用strings命令直接提取文件里的可打印字符串。板块名称、股票代码、板块描述这些信息通常都是以明文存在的先通过strings把文本部分捞出来确认大概的板块数量和字段顺序再对照二进制偏移去解析效率会高很多。对于多数量化需求来说如果只是想拿到某个板块的成分股列表更省力的办法是直接用同花顺客户端里导出自选股的功能或者从行情接口获取板块成分数据不必死磕板块文件的二进制结构。我自己的经验是数据文件解析够用就好不要为了“完整”而在非核心格式上耗费过多时间。5. 可以直接用的 Python 解析脚本前面铺垫了这么多终于到代码环节。我把完整的日线解析脚本整理如下大家可以直接复制使用。核心逻辑就是按 32 字节切分数据块用struct模块解包再换算成人类可读的数据结构。5.1 单文件解析import struct from datetime import date, timedelta BASE_DATE date(1990, 12, 19) RECORD_SIZE 32 MAGIC bytes.fromhex(5A240A02) def parse_day_file(filepath): records [] with open(filepath, rb) as f: data f.read() # 检查魔数判断文件类型 if data[:4] ! MAGIC: raise ValueError(f文件格式不匹配: {filepath}) # 跳过文件头从偏移 32 开始解析 offset 32 while offset RECORD_SIZE len(data): chunk data[offset:offset RECORD_SIZE] ( raw_date, open_price, high_price, low_price, close_price, volume, amount, reserved ) struct.unpack(iiiiiffi, chunk) records.append({ date: BASE_DATE timedelta(daysraw_date), open: open_price / 100.0, high: high_price / 100.0, low: low_price / 100.0, close: close_price / 100.0, volume: volume, # 单位股 amount: amount, # 单位元 reserved: reserved }) offset RECORD_SIZE return records这段代码有一个值得说明的细节struct.unpack(iiiiiffi, chunk)中的格式字符串i表示 4 字节有符号整数f表示 4 字节单精度浮点数表示小端序。日期、四个价格、保留字段用整数解包成交量和成交额用浮点解包与前面拆解的字段布局完全对应。5.2 批量解析整个目录实际使用时一般不会只解析一只股票而是要把整个市场的日线文件全部读进来。下面这个函数可以批量处理所有.day文件并按“市场/代码”结构存入字典import os from pathlib import Path def parse_market_dir(root_dir): root_dir 指向 history/day 目录 result {} for market in os.listdir(root_dir): market_path Path(root_dir) / market if not market_path.is_dir(): continue for file_path in market_path.glob(*.day): code file_path.stem # 例如 sh600000 try: result[code] parse_day_file(str(file_path)) except Exception as e: print(f解析失败: {file_path}, 错误: {e}) return result如果想把数据拿去做回测或进一步分析可以直接把这些记录转成 pandas DataFrame再按日期排序导出 CSV 或写入 SQLite 数据库。这一步属于工程细节代码比较简单就不再贴出来了。关于文件头偏移我再强调一遍上面的代码假设数据记录从偏移 32 开始。但若你手里的客户端版本较老文件头长度可能不同。批量解析前先用第一小节的方法验证魔数之后的数据起点根据实际调整offset的初始值。6. 实测中遇到的坑与应对方式解析过程不是一蹴而就的实际运行时会遇到各种意外情况。有些问题不真正跑一遍根本发现不了下面这些就是我在实测中实打实踩过的坑写出来帮大家提前避雷。6.1 文件锁与读写冲突同花顺客户端运行时会对部分历史数据文件添加读写锁尤其是正在下载或更新的股票文件。如果这时候你的解析脚本去读取可能会遇到两种情况要么读取权限被拒绝要么读出来的数据不完整。最稳妥的做法是解析前先关闭同花顺客户端或者把整个history目录先复制一份到别的位置再对副本做解析。我的习惯是在每天收盘后先用批处理脚本把历史目录增量同步到数据服务器然后在服务器上做解析这样既不干扰客户端使用也方便多台机器共用同一份行情数据。6.2 除权与复权问题文件里存的是裸数据同花顺本地日线文件保存的是未经复权处理的真实成交数据。这意味着如果你直接用解析出来的收盘价序列计算收益率遇到除权除息日会出现价格跳空影响回测结果。比如某股票 10 派 5 元除息日当天股价会直接下调如果不知道这个除息事件直接拿收盘价算收益会莫名其妙多出一段“亏损”。正确处理方式是另外维护一份除权除息日历在回测引擎里做复权处理或者拿到同花顺的复权因子数据后再加工。单靠.day文件的裸数据解决不了复权问题这一点在搭建数据管道时就要提前规划。6.3 文件头版本差异前面已经提到不同版本的同花顺写出的文件头长度和内容存在差异。最典型的场景是你自己电脑上安装的是 2024 年新版解析一切正常结果把脚本部署到同事的机器上那里装的是两年前的旧版解析出来的第一条记录全是乱码。应对方式就是在解析脚本里做“双保险”先检查魔数再检查第一条记录的日期字段是否落在合理区间比如 1990 到 2030 之间如果不在合理区间自动调整文件头偏移量重新解析。灵活处理版本差异的代码比硬编码一个固定偏移要可靠得多。6.4 性能优化单只股票的日线文件很小只有几 KB 到几十 KB无需考虑性能。但如果要全市场批量解析几千个文件加起来也有几百 MB 数据量这时候有几条优化思路可以参考使用mmap映射文件减少read()调用的系统开销一次性unpack整个文件而不要逐条迭代解析结果直接写入二进制格式如 parquet或数据库避免反复读写 CSV。我自己实测下来全市场日线文件用上面的脚本解析耗时在几秒到十几秒之间完全在可接受范围内。代码库里再配上增量更新机制每天盘后自动跑一遍数据管道就基本成型了。最后再分享一个小技巧解析出来的数据一定要先抽样验证再入库。我每次写完解析脚本都会随机挑三只股票把最后一行的收盘价和同花顺客户端显示的最新收盘价做对比确认分毫不差才继续跑全量。这个方法看起来简单但能避免绝大多数因格式变化导致的数据污染问题值得养成习惯。本文还有配套的精品资源点击获取