做量化开发的都懂交易日期判断看似是小细节却能直接决定策略成败 比如“周末误触发策略执行导致空单挂单失败”“漏算法定节假日抓取到空数据拖垮整个同步任务”“计算前后交易日时重复过滤节假日效率极低”…… 这些坑几乎每个量化开发者都踩过。交易日期判断的核心本质是“区分周末过滤法定节假日”但手动维护节假日列表、重复编写判断逻辑不仅耗时费力还容易出错。尤其是A股节假日每年更新手动同步更是麻烦到崩溃。今天带来Java量化系列第40篇实战——交易日期判断全流程落地方案从节假日数据定时同步、实体类封装到工具类一站式适配多场景需求、对外接口开发全套可运行代码直接奉上覆盖“判断是否交易日、查询前后交易日、统计区间交易日”等所有高频场景彻底解决量化开发中日期判断的痛点。一、核心认知交易日期判断的关键要点先理清核心逻辑避免开发走弯路明确量化场景下交易日期判断的核心诉求• 核心判断逻辑交易日 非周末非周六、周日 非法定节假日二者缺一不可。• 数据支撑需维护法定节假日列表每年更新一次避免手动录入出错通过定时任务自动同步无需人工干预。• 高频场景判断指定日期是否为交易日、获取前后N个交易日、统计区间内所有交易日、查询最近交易日列表适配策略执行、数据抓取等场景。• 核心优势封装通用工具类一键复用所有日期判断逻辑定时同步节假日数据适配每年节假日调整对外提供标准化接口支撑多服务调用。关键提醒A股交易日遵循“周末休市、法定节假日休市”规则无特殊调休补班调休上班日若为周末仍不算交易日本文方案完全贴合A股规则。二、核心实现从数据同步到工具类封装全流程⚙️整套方案分为“实体类封装-节假日数据定时同步-DateHelper工具类开发-对外接口开发”四步每一步都附完整代码详细注释所有代码可直接导入项目运行无需额外修改。2.1 实体类封装HolidayCalendarDo数据存储核心用于存储法定节假日数据关联数据库表holiday_calendar区分日期类型交易日/周末/法定节假日为后续判断提供数据支撑完整代码如下DataEqualsAndHashCode(callSuperfalse)TableName(holiday_calendar)// 关联数据库表publicclassHolidayCalendarDoimplementsSerializable{privatestaticfinallongserialVersionUID1L;/** 主键自增 */TableId(valueid,typeIdType.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 同步结果成功/失败/已存在 */publicOutputResultVoidsyncYear(Integeryear){// 1. 校验该年份数据是否已存在避免重复同步HolidayQueryParamqueryParamnewHolidayQueryParam();queryParam.setYear(year);ListHolidayCalendarDoholidayCalendarDoListholidayCalendarDomainService.listByCondition(queryParam);if(!CollUtil.isEmpty(holidayCalendarDoList)){log.info(已经存在 {}年的假期数据不需要同步,year);returnOutputResult.buildAlert(ResultCode.HOLIDAY_EXISTS);}Map?,?data;try{// 2. 调用第三方接口获取当年节假日数据接口返回格式年份-日期映射0工作日非0节假日datarestTemplate.getForObject(http://tool.bitefu.net/jiari/?dyear,Map.class);}catch(Exceptione){log.error(获取同步假期数据时出现异常,e);returnOutputResult.buildFail();}// 3. 解析接口返回数据封装为实体类列表MapString,IntegerdateInfo(MapString,Integer)data.get(String.valueOf(year));ListHolidayCalendarDolistdateInfo.entrySet().stream().filter(entry-entry.getValue()!0)// 筛选出节假日非0即为节假日.map(entry-{Datedate;try{// 格式化日期接口返回日期为MMdd拼接年份转为yyyyMMdd格式dateDateUtils.parseDate(yearentry.getKey(),yyyyMMdd);}catch(ParseExceptione){thrownewIllegalArgumentException(e);}HolidayCalendarDoholidayCalendarDonewHolidayCalendarDo();holidayCalendarDo.setHolidayDate(date);holidayCalendarDo.setCurrYear(year);holidayCalendarDo.setDateType(3);// 标记为法定节假日date_type3returnholidayCalendarDo;}).collect(Collectors.toList());// 4. 批量保存节假日数据到数据库holidayCalendarDomainService.saveBatch(list);returnOutputResult.buildSucc();}定时任务配置说明关键// 定时任务注解每年1月1日0点10分执行同步当年节假日数据Scheduled(cron0 10 0 1 1 ?)publicvoidsyncHolidayData(){intcurrentYearLocalDate.now().getYear();syncYear(currentYear);}避坑重点第三方接口返回日期为MMdd格式需拼接年份转为yyyyMMdd再解析避免日期格式错误添加异常捕获防止接口调用失败导致定时任务崩溃。对应的地址是:http://tool.bitefu.net/jiari/?d2026返回内容是:{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(currDatenull){currDateDateUtil.date();}// 1. 查询当前年份的节假日列表按年查询提升效率ListStringholidayDateListholidayCalendarService.listHolidayDateByYear(DateUtil.year(currDate));// 2. 先判断是否为周末是则直接返回falseif(DateUtil.isWeekend(currDate)){returnfalse;}// 3. 判断是否在节假日列表中不在则为交易日StringformatDateDateUtil.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 工作日的日期列表 */publicListDatebetweenWorkDay(DatestartDate,DateendDate){if(!(startDate!nullendDate!null)){returnCollections.emptyList();}// 格式化日期为当天开始/结束时间避免时间戳干扰startDateDateUtil.beginOfDay(startDate);endDateDateUtil.endOfDay(endDate);ListDateresultnewArrayList();// 循环遍历区间内所有日期筛选出交易日while(endDate.after(startDate)){if(isWorkingDay(startDate)){result.add(DateUtil.beginOfDay(startDate));}startDateDateUtil.offsetDay(startDate,1);// 日期加1天}returnresult;}工具类亮点所有方法均做了空值处理和效率优化按年查询节假日列表避免全表扫描日期格式化统一规避时间戳干扰适配多场景调用。3.4 对外接口开发支撑多服务调用基于工具类开发标准化对外接口覆盖前端查询、其他服务调用等场景接口文档清晰直接对接业务核心接口代码如下/** * 判断指定日期是否为交易日核心接口 * param day 日期字符串格式yyyy-MM-dd * return 1是交易日0非交易日 */Operation(summary查询某天是否是交易日)GetMapping(/getTradeDay/{day})publicOutputResultIntegergetTradeDay(PathVariable(day)Stringday){DatedateDateUtil.parse(day);returnOutputResult.buildSucc(dateHelper.isWorkingDay(date)?1:0);}/** * 查询最近十天的交易日日期列表 * return 最近10个交易日的日期字符串列表格式yyyy-MM-dd */Operation(summary查询最近十天的交易日日期列表)GetMapping(/getTenTradeDay)publicOutputResultListStringgetTenTradeDay(){returnholidayCalendarBusiness.getTenTradeDay();}/** * 查询日期段内的交易日日期列表 * param dateRo 日期对象包含开始日期、结束日期 * return 区间内所有交易日日期列表 */Operation(summary查询日期段内的交易日日期列表)PostMapping(/getTradeDay)publicOutputResultListStringgetTradeDay(RequestBodyDateRodateRo){returnholidayCalendarBusiness.getTradeDay(dateRo);}/** * 查询前N天的交易日日期不含当天 * param days 天数 * return 前N天的交易日日期 */Operation(summary查询前几天的对应日期)GetMapping(/getBeforeLastDay/{days})publicOutputResultDategetBeforeLastDay(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个交易日OutputResultListStringresultholidayCalendarBusiness.getTenTradeDay();if(!result.isSuccess()||CollUtil.isEmpty(result.getData())){log.error(查询交易日失败无法同步数据);return;}// 2. 循环抓取每个交易日的股票数据ListStringtradeDaysresult.getData();tradeDays.forEach(day-{stockDataService.syncDailyData(day);// 抓取单个交易日数据});}场景3统计区间内交易日数量回测场景// 回测统计入口publicintcountTradeDays(StringstartDateStr,StringendDateStr){// 1. 计算区间内所有交易日ListStringtradeDaysdateHelper.betweenWorkDay(startDateStr,endDateStr);// 2. 返回交易日数量returntradeDays.size();}五、避坑指南8个实战踩雷教训⚠️必看交易日期判断看似简单实则暗藏很多细节坑整理了实战中踩过的雷帮你少走弯路坑1日期格式不统一导致判断失效✅ 原因工具类中日期格式化与数据库存储格式不一致如工具类用yyyy-MM-dd数据库用yyyyMMdd导致节假日匹配失败。✅ 解决统一日期格式为yyyy-MM-ddConst.SIMPLE_DATE_FORMAT定义数据库与工具类保持一致。坑2定时任务执行时机错误遗漏节假日✅ 原因定时任务未在1月1日执行或执行时第三方接口未更新当年节假日数据导致数据缺失。✅ 解决定时任务设置为1月1日0点10分执行避开接口更新高峰期添加执行日志失败时触发告警。坑3未处理跨年节假日查询失败✅ 原因12月查询次年1月节假日时仅查询当年节假日列表导致跨年节假日未被过滤。✅ 解决工具类中添加跨年处理逻辑如12月查询时同时加载当年和次年节假日数据参考getAfterWorkingByCount方法实现。坑4频繁查询数据库性能瓶颈✅ 原因每次判断交易日都查询数据库高并发场景下导致服务卡顿。✅ 解决将当年节假日列表缓存到Redis设置过期时间为1年查询时先查缓存再查数据库。坑5日期为null未处理报空指针异常✅ 原因未处理currDatenull的情况直接调用日期工具类方法导致空指针。✅ 解决所有工具类方法均添加空值处理默认使用当前系统日期。坑6第三方接口调用失败未做兜底✅ 原因第三方接口宕机或超时未做降级处理导致定时任务崩溃。✅ 解决添加接口重试机制最多重试3次失败时手动同步接口数据避免任务中断。坑7混淆“交易日”与“工作日”✅ 原因误将调休上班日当作交易日调休上班日若为周末仍不算交易日。✅ 解决严格遵循“非周末非法定节假日”双重判断第三方接口已处理调休逻辑无需额外开发。坑8区间交易日统计遗漏起始日期✅ 原因循环判断时未包含起始日期导致统计结果少1天。✅ 解决将起始日期格式化为本日开始时间DateUtil.beginOfDay(startDate)确保起始日期被纳入判断。五、福利领取完整可运行代码包免费送为了帮大家快速落地我整理了本次交易日期判断实战的完整可运行代码包包含• ① holiday_calendar 对应的sql 语句 和近三年的数据。• ② DateHelper工具类完整代码所有日期判断方法• ③ 对外接口完整代码接口文档私信回复【交易日期判断】即可免费领取所有代码均可直接导入项目运行无需修改帮你节省1天以上开发时间彻底搞定量化开发中日期判断的所有难题。下期预告✨本次我们搞定了交易日期判断的全流程方案下期将聚焦 股票池获取涨停股票跌停股票 等数据 为量化策略筑牢数据基础敬请期待结尾互动你在量化开发中还遇到过哪些日期判断的坑欢迎在评论区留言讨论如果觉得这篇文章对你有帮助别忘了点赞在看转发让更多量化开发者告别日期判断的困扰 关注我持续解锁Java量化实战干货