news 2026/5/3 11:58:29

Java量化系列(四十): 一键搞定节假日和周末,交易日期判断终极方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java量化系列(四十): 一键搞定节假日和周末,交易日期判断终极方案

做量化开发的都懂,交易日期判断看似是小细节,却能直接决定策略成败!🤯 比如:

“周末误触发策略执行,导致空单挂单失败”“漏算法定节假日,抓取到空数据拖垮整个同步任务”“计算前后交易日时,重复过滤节假日效率极低”…… 这些坑,几乎每个量化开发者都踩过。

交易日期判断的核心,本质是“区分周末+过滤法定节假日”,但手动维护节假日列表、重复编写判断逻辑,不仅耗时费力,还容易出错。尤其是A股节假日每年更新,手动同步更是麻烦到崩溃。

今天带来Java量化系列第40篇实战——交易日期判断全流程落地方案,从节假日数据定时同步、实体类封装,到工具类一站式适配多场景需求、对外接口开发,全套可运行代码直接奉上,覆盖“判断是否交易日、查询前后交易日、统计区间交易日”等所有高频场景,彻底解决量化开发中日期判断的痛点。

一、核心认知:交易日期判断的关键要点🎯

先理清核心逻辑,避免开发走弯路,明确量化场景下交易日期判断的核心诉求:

  • • 🔹 核心判断逻辑:交易日 = 非周末(非周六、周日)+ 非法定节假日,二者缺一不可。
  • • 🔹 数据支撑:需维护法定节假日列表,每年更新一次,避免手动录入出错;通过定时任务自动同步,无需人工干预。
  • • 🔹 高频场景:判断指定日期是否为交易日、获取前后N个交易日、统计区间内所有交易日、查询最近交易日列表(适配策略执行、数据抓取等场景)。
  • • 🔹 核心优势:封装通用工具类,一键复用所有日期判断逻辑;定时同步节假日数据,适配每年节假日调整;对外提供标准化接口,支撑多服务调用。

关键提醒:A股交易日遵循“周末休市、法定节假日休市”规则,无特殊调休补班(调休上班日若为周末,仍不算交易日),本文方案完全贴合A股规则。

二、核心实现:从数据同步到工具类封装全流程⚙️

整套方案分为“实体类封装-节假日数据定时同步-DateHelper工具类开发-对外接口开发”四步,每一步都附完整代码+详细注释,所有代码可直接导入项目运行,无需额外修改。

2.1 实体类封装:HolidayCalendarDo(数据存储核心)

用于存储法定节假日数据,关联数据库表holiday_calendar,区分日期类型(交易日/周末/法定节假日),为后续判断提供数据支撑,完整代码如下:

@Data@EqualsAndHashCode(callSuper=false)@TableName("holiday_calendar")// 关联数据库表publicclassHolidayCalendarDoimplementsSerializable{privatestaticfinallongserialVersionUID=1L;/** 主键自增 */@TableId(value="id",type=IdType.AUTO)privateIntegerid;/** 法定日期,不开盘(存储节假日日期) */@TableField("holiday_date")privateDateholidayDate;/** 当前年(用于按年筛选,提升查询效率) */@TableField("curr_year")privateIntegercurrYear;/** 日期类型 3为法定节假日 */@TableField("date_type")privateIntegerdateType;}

设计亮点:按年存储节假日数据,查询时可精准筛选对应年份的节假日,避免全表扫描;日期类型字段只放置 法定节假日。

2.2 定时任务:每年自动同步节假日数据(解放双手)

核心需求:每年1月1日自动同步当年法定节假日数据,避免重复同步,无需人工维护。通过syncYear方法调用第三方接口获取数据,批量保存到数据库,完整代码如下:

/** * 同步指定年份的节假日数据(定时任务每年1月1日执行) * @param year 需同步的年份 * @return 同步结果(成功/失败/已存在) */publicOutputResult<Void>syncYear(Integeryear){// 1. 校验该年份数据是否已存在,避免重复同步HolidayQueryParamqueryParam=newHolidayQueryParam();queryParam.setYear(year);List<HolidayCalendarDo>holidayCalendarDoList=holidayCalendarDomainService.listByCondition(queryParam);if(!CollUtil.isEmpty(holidayCalendarDoList)){log.info(">>>已经存在 {}年的假期数据,不需要同步",year);returnOutputResult.buildAlert(ResultCode.HOLIDAY_EXISTS);}Map<?,?>data;try{// 2. 调用第三方接口获取当年节假日数据(接口返回格式:年份->日期映射,0=工作日,非0=节假日)data=restTemplate.getForObject("http://tool.bitefu.net/jiari/?d="+year,Map.class);}catch(Exceptione){log.error("获取同步假期数据时出现异常",e);returnOutputResult.buildFail();}// 3. 解析接口返回数据,封装为实体类列表Map<String,Integer>dateInfo=(Map<String,Integer>)data.get(String.valueOf(year));List<HolidayCalendarDo>list=dateInfo.entrySet().stream().filter(entry->entry.getValue()!=0)// 筛选出节假日(非0即为节假日).map(entry->{Datedate;try{// 格式化日期(接口返回日期为MMdd,拼接年份转为yyyyMMdd格式)date=DateUtils.parseDate(year+entry.getKey(),"yyyyMMdd");}catch(ParseExceptione){thrownewIllegalArgumentException(e);}HolidayCalendarDoholidayCalendarDo=newHolidayCalendarDo();holidayCalendarDo.setHolidayDate(date);holidayCalendarDo.setCurrYear(year);holidayCalendarDo.setDateType(3);// 标记为法定节假日(date_type=3)returnholidayCalendarDo;}).collect(Collectors.toList());// 4. 批量保存节假日数据到数据库holidayCalendarDomainService.saveBatch(list);returnOutputResult.buildSucc();}

定时任务配置说明(关键):

// 定时任务注解,每年1月1日0点10分执行,同步当年节假日数据@Scheduled(cron="0 10 0 1 1 ?")publicvoidsyncHolidayData(){intcurrentYear=LocalDate.now().getYear();syncYear(currentYear);}

避坑重点:第三方接口返回日期为MMdd格式,需拼接年份转为yyyyMMdd再解析,避免日期格式错误;添加异常捕获,防止接口调用失败导致定时任务崩溃。

对应的地址是:

http://tool.bitefu.net/jiari/?d=2026

返回内容是:

{2026:{1001:2,1002:2,1003:2,1004:1,1005:1,1006:1,1007:1,0101:2,0102:1,0103:1,0215:1,0216:2,0217:2,0218:2,0219:2,0220:1,0221:1,0222:1,0223:1,0404:1,0405:2,0406:1,0501:2,0502:2,0503:1,0504:1,0505:1,0619:2,0620:1,0621:1,0925:2,0926:1,0927:1}}

3. 核心工具类:DateHelper(一站式日期判断解决方案)

封装所有交易日期相关工具方法,覆盖量化开发全场景,无需重复编写判断逻辑,直接调用即可。核心方法分类讲解,完整代码附注释:

3.1 基础判断:是否为交易日

核心方法isWorkingDay,判断指定日期是否为交易日(非周末+非法定节假日),适配策略执行、数据抓取等基础场景:

/** * 判断指定时间是否为工作日(非周末且不在节假日列表中) * @param currDate 指定的时间(为null则使用当前系统日期) * @return 当前时间是否是工作日, 是为 true, 否则为 false */publicbooleanisWorkingDay(DatecurrDate){if(currDate==null){currDate=DateUtil.date();}// 1. 查询当前年份的节假日列表(按年查询,提升效率)List<String>holidayDateList=holidayCalendarService.listHolidayDateByYear(DateUtil.year(currDate));// 2. 先判断是否为周末,是则直接返回falseif(DateUtil.isWeekend(currDate)){returnfalse;}// 3. 判断是否在节假日列表中,不在则为交易日StringformatDate=DateUtil.format(currDate,Const.SIMPLE_DATE_FORMAT);return!holidayDateList.contains(formatDate);}

3.2 场景化工具:前后交易日查询

覆盖“获取前N个交易日、后N个交易日、排除当天的前后交易日”等高频场景,以两个核心方法为例,其余方法可直接复用:

/** * 查询距离指定日期N天前的工作日日期(包含当天,不包含假期与周末) * @param date 日期 * @param days 天数 * @return N天前的工作日 */publicDategetBeforeWorkingDateByDay(Datedate,intdays){returnDateUtil.beginOfDay(getBeforeWorkingByCount(date,days,true).getFirst());}/** * 查询距离指定日期N天前的工作日日期(不包含当天) * @param date 日期 * @param days 天数 * @return N天前的工作日(不含当天) */publicDategetBeforeWorkingDateByDayRemoveToday(Datedate,intdays){returnDateUtil.beginOfDay(getBeforeWorkingByCountRemoveToday(date,days,true).getFirst());}

3.3 进阶工具:区间交易日统计

计算两个日期之间的所有交易日,适配区间数据统计、策略回测等场景,核心方法如下:

/** * 计算两个日期之间的所有工作日(包含起始日期) * @param startDate 开始日期 * @param endDate 结束日期 * @return 工作日的日期列表 */publicList<Date>betweenWorkDay(DatestartDate,DateendDate){if(!(startDate!=null&&endDate!=null)){returnCollections.emptyList();}// 格式化日期为当天开始/结束时间,避免时间戳干扰startDate=DateUtil.beginOfDay(startDate);endDate=DateUtil.endOfDay(endDate);List<Date>result=newArrayList<>();// 循环遍历区间内所有日期,筛选出交易日while(endDate.after(startDate)){if(isWorkingDay(startDate)){result.add(DateUtil.beginOfDay(startDate));}startDate=DateUtil.offsetDay(startDate,1);// 日期加1天}returnresult;}

工具类亮点:所有方法均做了空值处理和效率优化,按年查询节假日列表避免全表扫描;日期格式化统一,规避时间戳干扰,适配多场景调用。

3.4 对外接口开发:支撑多服务调用

基于工具类开发标准化对外接口,覆盖前端查询、其他服务调用等场景,接口文档清晰,直接对接业务,核心接口代码如下:

/** * 判断指定日期是否为交易日(核心接口) * @param day 日期字符串(格式:yyyy-MM-dd) * @return 1=是交易日,0=非交易日 */@Operation(summary="查询某天是否是交易日")@GetMapping("/getTradeDay/{day}")publicOutputResult<Integer>getTradeDay(@PathVariable("day")Stringday){Datedate=DateUtil.parse(day);returnOutputResult.buildSucc(dateHelper.isWorkingDay(date)?1:0);}/** * 查询最近十天的交易日日期列表 * @return 最近10个交易日的日期字符串列表(格式:yyyy-MM-dd) */@Operation(summary="查询最近十天的交易日日期列表")@GetMapping("/getTenTradeDay")publicOutputResult<List<String>>getTenTradeDay(){returnholidayCalendarBusiness.getTenTradeDay();}/** * 查询日期段内的交易日日期列表 * @param dateRo 日期对象(包含开始日期、结束日期) * @return 区间内所有交易日日期列表 */@Operation(summary="查询日期段内的交易日日期列表")@PostMapping("/getTradeDay")publicOutputResult<List<String>>getTradeDay(@RequestBodyDateRodateRo){returnholidayCalendarBusiness.getTradeDay(dateRo);}/** * 查询前N天的交易日日期(不含当天) * @param days 天数 * @return 前N天的交易日日期 */@Operation(summary="查询前几天的对应日期")@GetMapping("/getBeforeLastDay/{days}")publicOutputResult<Date>getBeforeLastDay(@PathVariableIntegerdays){returnholidayCalendarBusiness.getBeforeLastDay(days);}

接口使用场景:前端页面交易日筛选、量化策略调度(仅在交易日执行)、数据同步任务(仅抓取交易日数据)、回测系统日期校验等,通用性极强。

大家可以通过:

https://stock-api.apifox.cn/api-391134756

看响应的数据,进行理解

四、实战场景:工具类+接口的落地案例📊

结合量化开发高频场景,举例说明如何使用本文方案,帮你快速落地:

场景1:量化策略仅在交易日执行

// 策略执行入口publicvoidexecuteStrategy(){// 1. 判断当前日期是否为交易日if(!dateHelper.isWorkingDay(null)){log.info("今日非交易日,不执行策略");return;}// 2. 执行策略逻辑(选股、下单、复盘等)strategyService.run();}

场景2:抓取最近10个交易日的股票数据

// 数据抓取入口publicvoidsyncStockData(){// 1. 查询最近10个交易日OutputResult<List<String>>result=holidayCalendarBusiness.getTenTradeDay();if(!result.isSuccess()||CollUtil.isEmpty(result.getData())){log.error("查询交易日失败,无法同步数据");return;}// 2. 循环抓取每个交易日的股票数据List<String>tradeDays=result.getData();tradeDays.forEach(day->{stockDataService.syncDailyData(day);// 抓取单个交易日数据});}

场景3:统计区间内交易日数量(回测场景)

// 回测统计入口publicintcountTradeDays(StringstartDateStr,StringendDateStr){// 1. 计算区间内所有交易日List<String>tradeDays=dateHelper.betweenWorkDay(startDateStr,endDateStr);// 2. 返回交易日数量returntradeDays.size();}

五、避坑指南:8个实战踩雷教训⚠️(必看)

交易日期判断看似简单,实则暗藏很多细节坑,整理了实战中踩过的雷,帮你少走弯路:

坑1:日期格式不统一,导致判断失效

✅ 原因:工具类中日期格式化与数据库存储格式不一致(如工具类用yyyy-MM-dd,数据库用yyyyMMdd),导致节假日匹配失败。

✅ 解决:统一日期格式为yyyy-MM-dd(Const.SIMPLE_DATE_FORMAT定义),数据库与工具类保持一致。

坑2:定时任务执行时机错误,遗漏节假日

✅ 原因:定时任务未在1月1日执行,或执行时第三方接口未更新当年节假日数据,导致数据缺失。

✅ 解决:定时任务设置为1月1日0点10分执行(避开接口更新高峰期),添加执行日志,失败时触发告警。

坑3:未处理跨年节假日,查询失败

✅ 原因:12月查询次年1月节假日时,仅查询当年节假日列表,导致跨年节假日未被过滤。

✅ 解决:工具类中添加跨年处理逻辑(如12月查询时,同时加载当年和次年节假日数据),参考getAfterWorkingByCount方法实现。

坑4:频繁查询数据库,性能瓶颈

✅ 原因:每次判断交易日都查询数据库,高并发场景下导致服务卡顿。

✅ 解决:将当年节假日列表缓存到Redis,设置过期时间为1年,查询时先查缓存,再查数据库。

坑5:日期为null未处理,报空指针异常

✅ 原因:未处理currDate=null的情况,直接调用日期工具类方法,导致空指针。

✅ 解决:所有工具类方法均添加空值处理,默认使用当前系统日期。

坑6:第三方接口调用失败,未做兜底

✅ 原因:第三方接口宕机或超时,未做降级处理,导致定时任务崩溃。

✅ 解决:添加接口重试机制(最多重试3次),失败时手动同步接口数据,避免任务中断。

坑7:混淆“交易日”与“工作日”

✅ 原因:误将调休上班日当作交易日(调休上班日若为周末,仍不算交易日)。

✅ 解决:严格遵循“非周末+非法定节假日”双重判断,第三方接口已处理调休逻辑,无需额外开发。

坑8:区间交易日统计遗漏起始日期

✅ 原因:循环判断时未包含起始日期,导致统计结果少1天。

✅ 解决:将起始日期格式化为本日开始时间(DateUtil.beginOfDay(startDate)),确保起始日期被纳入判断。

五、福利领取:完整可运行代码包免费送🎁

为了帮大家快速落地,我整理了本次交易日期判断实战的完整可运行代码包,包含:

  • • ① holiday_calendar 对应的sql 语句 和近三年的数据。
  • • ② DateHelper工具类完整代码(所有日期判断方法);
  • • ③ 对外接口完整代码+接口文档;

私信回复【交易日期判断】,即可免费领取!所有代码均可直接导入项目运行,无需修改,帮你节省1天以上开发时间,彻底搞定量化开发中日期判断的所有难题。

下期预告✨

本次我们搞定了交易日期判断的全流程方案,下期将聚焦 股票池,获取涨停股票,跌停股票 等数据 为量化策略筑牢数据基础,敬请期待!

结尾互动

你在量化开发中,还遇到过哪些日期判断的坑?欢迎在评论区留言讨论!

如果觉得这篇文章对你有帮助,别忘了点赞+在看+转发,让更多量化开发者告别日期判断的困扰~ 关注我,持续解锁Java量化实战干货!🚀

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

【Django毕设全套源码+文档】基于django的羽毛球服务管理系统的设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/1 18:55:17

【Django毕设源码分享】基于 Python 的教育题包综合处理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/3 9:10:43

【Django毕设源码分享】基于Django的B_S架构的球类赛事发布和在线购票系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/1 18:58:41

杨洋《雨霖铃》预约破百万 新释打戏花絮展南侠风骨

2月10日&#xff0c;由杨洋领衔主演的古装武侠剧《雨霖铃》优酷站内预约人数强势突破百万大关&#xff0c;观众期待值持续拉满。该剧以北宋为背景&#xff0c;讲述了以杨洋饰演的“南侠”展昭为核心的一众江湖志士&#xff0c;于诡谲朝局与武林纷争中坚守道义、护卫家国的侠义传…

作者头像 李华
网站建设 2026/5/1 0:25:26

【Django毕设全套源码+文档】基于django的智慧农业农产品销售及农机设备管理系统的设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华