
简介本资源面向金融系统开发、量化交易及数据仓库从业者提供2025年沪深股票证券交易日与非交易日的完整数据方案解决交易日历维护与数据库导入的实际问题。压缩包共7个文件约431KB包含3个SQL脚本、2个TXT说明、1个XLSX表格及1张休市安排图分别用于建表、数据导入、日历查阅与规则对照。其中DML脚本只需按自身表结构调整表名与字段即可直接执行Excel日历便于快速核对全年开市与休市日期通知原文与查询接口说明则帮助理解节假日安排依据。已有266人学习下载适合需要将交易日数据落地到业务库、或搭建行情调度与结算系统的开发者参考使用。1. 2025年沪深股票证券交易日Insert-SQL脚本及Excel数据从数据源到入库的完整路径每年11月中下旬交易所会公布下一年的休市安排。做量化回测、交易日历校验、定时任务调度的团队都需要把这份安排转成结构化数据。2025年沪深股票证券交易日Insert-SQL脚本及Excel数据本质上就是解决一件事把散落在公告里的休市日期变成可以直接入库、可以直接查询、可以直接参与业务逻辑判断的交易日历表。它适合三类人一是做回测框架、需要精确交易日切片的量化开发者二是做数据仓库、需要维护日期维度表的数仓工程师三是做定时任务、需要判断“今天是不是交易日”的后端开发。Excel负责人工核对和快速分发Insert-SQL脚本负责批量入库和版本管理两者配合才是完整方案。2. 交易日历表怎么设计字段、主键与节假日标记2.1 最小可用表结构交易日历表不需要复杂设计但字段语义必须清晰。常见做法是建一张trade_calendar表核心字段包括日期、是否交易日、交易所标识、星期几、备注。主键用「日期 交易所」组合因为沪市和深市在绝大多数年份休市安排一致但极端情况下如2020年春节延长需要分别记录。CREATE TABLE trade_calendar ( cal_date DATE NOT NULL, exchange VARCHAR(10) NOT NULL, -- SSE上交所, SZSE深交所 is_trading_day TINYINT NOT NULL, -- 1交易日, 0休市 weekday TINYINT NOT NULL, -- 1周一 ... 7周日 remark VARCHAR(50) DEFAULT NULL, PRIMARY KEY (cal_date, exchange) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;is_trading_day用 TINYINT 而不是 BOOLEAN是为了兼容不同数据库的布尔类型差异。weekday冗余存储是为了避免查询时反复调用日期函数在批量生成年度日历时一次性算好。remark用来标注「春节」「国庆」等方便人工排查。2.2 生成全年日期骨架不要手工敲365行日期。用递归 CTE 或数字辅助表生成全年日期再根据周末规则打上初始标记。-- MySQL 8.0 递归 CTE 生成 2025 全年日期 WITH RECURSIVE date_series AS ( SELECT DATE(2025-01-01) AS dt UNION ALL SELECT DATE_ADD(dt, INTERVAL 1 DAY) FROM date_series WHERE dt DATE(2025-12-31) ) SELECT dt AS cal_date, SSE AS exchange, CASE WHEN DAYOFWEEK(dt) IN (1, 7) THEN 0 ELSE 1 END AS is_trading_day, CASE WHEN DAYOFWEEK(dt) 1 THEN 7 ELSE DAYOFWEEK(dt) - 1 END AS weekday, NULL AS remark FROM date_series;DAYOFWEEK在 MySQL 中返回1周日、7周六所以IN (1,7)判断周末。weekday字段转换成1周一、7周日的习惯表示。这一步只处理周末法定节假日需要下一步覆盖。2.3 用 INSERT ... ON DUPLICATE KEY UPDATE 覆盖节假日拿到交易所公布的休市安排后把节假日日期批量更新为is_trading_day 0。用INSERT ... ON DUPLICATE KEY UPDATE可以同时处理「新增」和「覆盖」两种情况避免先删后插导致的主键冲突。INSERT INTO trade_calendar (cal_date, exchange, is_trading_day, weekday, remark) VALUES (2025-01-01, SSE, 0, 3, 元旦), (2025-01-28, SSE, 0, 2, 春节), (2025-01-29, SSE, 0, 3, 春节), (2025-01-30, SSE, 0, 4, 春节), (2025-01-31, SSE, 0, 5, 春节), (2025-02-03, SSE, 0, 1, 春节), (2025-02-04, SSE, 0, 2, 春节), (2025-04-04, SSE, 0, 5, 清明节), (2025-05-01, SSE, 0, 4, 劳动节), (2025-05-02, SSE, 0, 5, 劳动节), (2025-05-05, SSE, 0, 1, 劳动节), (2025-06-02, SSE, 0, 1, 端午节), (2025-10-01, SSE, 0, 3, 国庆节), (2025-10-02, SSE, 0, 4, 国庆节), (2025-10-03, SSE, 0, 5, 国庆节), (2025-10-06, SSE, 0, 1, 国庆节), (2025-10-07, SSE, 0, 2, 国庆节), (2025-10-08, SSE, 0, 3, 国庆节) ON DUPLICATE KEY UPDATE is_trading_day VALUES(is_trading_day), remark VALUES(remark);VALUES()函数在 MySQL 8.0.20 之后被标记为废弃新版本可以用别名语法AS new ON DUPLICATE KEY UPDATE is_trading_day new.is_trading_day。如果用的是 SQL Server对应写法是MERGE语句逻辑相同但语法差异较大。注意节假日列表必须和交易所公告逐条核对。2025年春节休市从1月28日到2月4日但1月26日周日和2月8日周六是调休上班日这两天虽然名义上是周末但交易所是否开市需要看公告——实际上交易所周末从不开市调休上班日也不开市所以不需要特殊处理。3. Excel数据怎么组织从人工核对到批量导入3.1 Excel模板的列设计Excel文件承担两个职责一是给业务方人工查看和确认二是作为批量导入的数据源。列设计要和数据库表字段对应但可以增加辅助列方便人工阅读。列名类型说明cal_date日期格式 YYYY-MM-DDexchange文本SSE 或 SZSEis_trading_day数字1 或 0weekday数字1-7remark文本节假日名称交易日留空year数字2025方便筛选month数字1-12方便透视year和month是冗余列但在Excel里做数据透视和筛选时非常实用。不要用Excel公式动态计算直接存静态值避免打开文件时重算导致卡顿。3.2 用Python生成Excel并写入UUID如果交易日历需要和外部系统对接每行可能需要一个唯一标识。用Python的openpyxl或xlsxwriter生成Excel时可以顺便写入UUID。import uuid from openpyxl import Workbook from datetime import date, timedelta wb Workbook() ws wb.active ws.title trade_calendar ws.append([uuid, cal_date, exchange, is_trading_day, weekday, remark]) start date(2025, 1, 1) end date(2025, 12, 31) current start while current end: is_weekend current.weekday() 5 ws.append([ str(uuid.uuid4()), current.strftime(%Y-%m-%d), SSE, 0 if is_weekend else 1, current.isoweekday(), ]) current timedelta(days1) wb.save(trade_calendar_2025.xlsx)uuid.uuid4()生成随机UUID适合做行级唯一标识。isoweekday()返回1周一、7周日和数据库字段定义一致。strftime(%Y-%m-%d)保证日期格式统一避免Excel自动转换成序列号。3.3 从Excel批量生成Insert语句如果不想用Python直接连数据库可以用Excel公式拼接Insert语句。在Excel新增一列写公式INSERT INTO trade_calendar (cal_date, exchange, is_trading_day, weekday, remark) VALUES (A2, B2, C2, D2, E2);下拉填充后复制整列粘贴到SQL客户端执行。这种方法适合一次性导入但不适合频繁更新。更稳妥的做法是用LOAD DATA INFILE或OPENROWSET直接读CSV。-- MySQL 从 CSV 导入 LOAD DATA LOCAL INFILE /path/to/trade_calendar_2025.csv INTO TABLE trade_calendar FIELDS TERMINATED BY , LINES TERMINATED BY \n IGNORE 1 ROWS (cal_date, exchange, is_trading_day, weekday, remark);IGNORE 1 ROWS跳过表头。LOCAL关键字允许从客户端读取文件但需要服务端开启local_infile。如果数据量不大用Excel公式拼接反而更直接。4. 避坑与排查交易日历入库的5个血泪教训4.1 日期格式不统一导致主键冲突现象导入时报Duplicate entry 2025-01-01-SSE但明明只导入了一次。原因Excel里日期列有的单元格是文本格式2025/1/1有的是日期格式2025-01-01导入后数据库识别成不同值。解决在Excel里统一用TEXT(A2,YYYY-MM-DD)转换或者在SQL导入时用STR_TO_DATE强制转换。更彻底的做法是在数据库层用DATE类型让数据库自己做规范化。4.2 时区问题导致日期偏移现象Python生成的Excel日期比预期少一天或多一天。原因服务器时区是UTCPython的date.today()返回UTC日期而业务预期是北京时间。解决显式指定时区用datetime.now(ZoneInfo(Asia/Shanghai)).date()获取日期。或者在生成Excel时直接硬编码年份不依赖系统时间。4.3 节假日调休日被误标为交易日现象2025年1月26日周日被标记为交易日但实际交易所不开市。原因只判断了周末没有覆盖调休上班日。调休上班日虽然是工作日但交易所周末从不开市。解决交易日历的规则是「周一至周五且不在法定节假日列表内」。调休上班日如果是周六周日仍然不是交易日。不要用「国家法定工作日」来替代「交易所交易日」。4.4 Excel公式下拉失效现象在Excel里写好了Insert拼接公式下拉填充时下面的单元格全是同样的值。原因Excel的自动计算被关闭或者公式引用用了绝对引用$A$2。解决检查「公式」选项卡下的「计算选项」是否设为「自动」。相对引用用A2下拉时会自动变成A3、A4。如果还是不行按Ctrl Alt F9强制重算所有工作表。4.5 SQL Server 的 MERGE 语句并发问题现象用 MERGE 批量更新交易日历时偶尔报主键冲突。原因MERGE 在高并发下存在已知的竞态条件多个会话同时执行时可能插入重复行。解决交易日历这种低频更新场景直接用INSERT ... ON DUPLICATE KEY UPDATEMySQL或INSERT ... ON CONFLICT DO UPDATEPostgreSQL。SQL Server 可以用IF NOT EXISTS ... INSERT ELSE UPDATE配合事务和WITH (HOLDLOCK)提示。5. 进阶用法用窗口函数校验交易日连续性5.1 找出缺失的交易日交易日历入库后需要验证是否有遗漏。用窗口函数可以快速找出两个交易日之间的间隔是否超过预期。SELECT cal_date, LAG(cal_date) OVER (ORDER BY cal_date) AS prev_trading_day, DATEDIFF(cal_date, LAG(cal_date) OVER (ORDER BY cal_date)) AS gap_days FROM trade_calendar WHERE exchange SSE AND is_trading_day 1 HAVING gap_days 3;正常情况下两个交易日之间最多间隔3天周五到周一。如果gap_days大于3说明中间有非周末的休市日没有被正确标记或者数据缺失。LAG函数在 MySQL 8.0、SQL Server 2012、PostgreSQL 中都支持。5.2 用 Excel 做快速交叉验证把数据库查询结果导出为CSV用Excel的VLOOKUP或XLOOKUP和交易所公告的日期列表做比对。在Excel里新增一列IF(ISNA(XLOOKUP(A2, 公告日期列, 公告日期列)), 缺失, 已覆盖)XLOOKUP在 Office 365 和 Office 2021 中可用老版本用VLOOKUP配合IFERROR。这一步能抓住90%以上的手工录入错误。5.3 一个我常用的习惯每年12月交易所公告出来后我会先手工在Excel里把休市日期标黄然后用Python脚本生成全年日期骨架再用SQL批量覆盖。最后跑一遍窗口函数校验确认没有超过3天的间隔。这个流程跑了三年唯一一次翻车是有一年春节调休日忘了排除导致回测时多算了一个交易日。从那以后我养成了一个习惯任何交易日历数据入库后先用SELECT COUNT(*) FROM trade_calendar WHERE is_trading_day 1 AND YEAR(cal_date) 2025数一下交易日总数和交易所公布的年度交易日天数对一下。2025年沪深两市全年交易日是243天如果数出来不是这个数说明数据有问题。这个数字比任何校验脚本都直接。希望帮到你。本文还有配套的精品资源点击获取