Files
vf_react/src/utils/tradingDayUtils.js
2025-10-11 12:02:01 +08:00

214 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/utils/tradingDayUtils.js
// 交易日计算工具函数
/**
* 中国股市交易日工具类
* 包含节假日判断和交易日计算
*/
class TradingDayUtils {
constructor() {
// 2024-2025年的法定节假日需要定期更新
this.holidays = new Set([
// 2024年节假日
'2024-01-01', // 元旦
'2024-02-09', '2024-02-10', '2024-02-11', '2024-02-12', '2024-02-13',
'2024-02-14', '2024-02-15', '2024-02-16', '2024-02-17', // 春节
'2024-04-04', '2024-04-05', '2024-04-06', // 清明节
'2024-05-01', '2024-05-02', '2024-05-03', '2024-05-04', '2024-05-05', // 劳动节
'2024-06-08', '2024-06-09', '2024-06-10', // 端午节
'2024-09-15', '2024-09-16', '2024-09-17', // 中秋节
'2024-10-01', '2024-10-02', '2024-10-03', '2024-10-04',
'2024-10-05', '2024-10-06', '2024-10-07', // 国庆节
// 2025年节假日预估需要根据官方公告更新
'2025-01-01', // 元旦
'2025-01-28', '2025-01-29', '2025-01-30', '2025-01-31',
'2025-02-01', '2025-02-02', '2025-02-03', '2025-02-04', // 春节
'2025-04-04', '2025-04-05', '2025-04-06', // 清明节
'2025-05-01', '2025-05-02', '2025-05-03', // 劳动节
'2025-05-31', '2025-06-01', '2025-06-02', // 端午节
'2025-10-01', '2025-10-02', '2025-10-03', '2025-10-04',
'2025-10-05', '2025-10-06', '2025-10-07', '2025-10-08', // 国庆节+中秋节
]);
// A股交易时间
this.marketOpenTime = { hour: 9, minute: 30 };
this.marketCloseTime = { hour: 15, minute: 0 };
}
/**
* 判断是否为周末
* @param {Date} date
* @returns {boolean}
*/
isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6; // 0是周日6是周六
}
/**
* 判断是否为节假日
* @param {Date} date
* @returns {boolean}
*/
isHoliday(date) {
const dateStr = this.formatDate(date);
return this.holidays.has(dateStr);
}
/**
* 判断是否为交易日
* @param {Date} date
* @returns {boolean}
*/
isTradingDay(date) {
return !this.isWeekend(date) && !this.isHoliday(date);
}
/**
* 格式化日期为 YYYY-MM-DD
* @param {Date} date
* @returns {string}
*/
formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
/**
* 判断时间是否在交易时间内
* @param {Date} datetime
* @returns {boolean}
*/
isInTradingHours(datetime) {
const hours = datetime.getHours();
const minutes = datetime.getMinutes();
// 上午交易时间9:30-11:30
const morningStart = hours > 9 || (hours === 9 && minutes >= 30);
const morningEnd = hours < 11 || (hours === 11 && minutes <= 30);
// 下午交易时间13:00-15:00
const afternoonStart = hours >= 13;
const afternoonEnd = hours < 15;
return (morningStart && morningEnd) || (afternoonStart && afternoonEnd);
}
/**
* 判断事件是否发生在交易时间后
* @param {Date} datetime
* @returns {boolean}
*/
isAfterTradingHours(datetime) {
const hours = datetime.getHours();
const minutes = datetime.getMinutes();
return hours > 15 || (hours === 15 && minutes > 0);
}
/**
* 获取下一个交易日
* @param {Date} date
* @returns {Date}
*/
getNextTradingDay(date) {
const nextDay = new Date(date);
nextDay.setDate(nextDay.getDate() + 1);
while (!this.isTradingDay(nextDay)) {
nextDay.setDate(nextDay.getDate() + 1);
}
return nextDay;
}
/**
* 获取上一个交易日
* @param {Date} date
* @returns {Date}
*/
getPreviousTradingDay(date) {
const prevDay = new Date(date);
prevDay.setDate(prevDay.getDate() - 1);
while (!this.isTradingDay(prevDay)) {
prevDay.setDate(prevDay.getDate() - 1);
}
return prevDay;
}
/**
* 根据事件时间获取对应的交易日
* 规则:
* 1. 如果是交易日的交易时间内,返回当天
* 2. 如果是交易日的15:00后返回下一个交易日
* 3. 如果是非交易日(周末或节假日),返回下一个交易日
*
* @param {Date|string} eventDateTime 事件时间
* @returns {string} 交易日期 YYYY-MM-DD
*/
getEffectiveTradingDay(eventDateTime) {
const datetime = typeof eventDateTime === 'string' ? new Date(eventDateTime) : eventDateTime;
// 如果是非交易日,直接返回下一个交易日
if (!this.isTradingDay(datetime)) {
return this.formatDate(this.getNextTradingDay(datetime));
}
// 如果是交易日
// 检查是否在15:00之后
if (this.isAfterTradingHours(datetime)) {
// 15:00后返回下一个交易日
return this.formatDate(this.getNextTradingDay(datetime));
}
// 交易日的15:00前返回当天
return this.formatDate(datetime);
}
/**
* 批量加载交易日数据如果有CSV文件
* @param {string} csvContent CSV内容
*/
loadTradingDaysFromCSV(csvContent) {
const lines = csvContent.trim().split('\n');
const tradingDays = new Set();
// 跳过标题行
for (let i = 1; i < lines.length; i++) {
const date = lines[i].trim();
if (date) {
// 转换日期格式 2010/1/4 -> 2010-01-04
const parts = date.split('/');
if (parts.length === 3) {
const year = parts[0];
const month = parts[1].padStart(2, '0');
const day = parts[2].padStart(2, '0');
tradingDays.add(`${year}-${month}-${day}`);
}
}
}
this.tradingDaysSet = tradingDays;
}
/**
* 使用CSV数据判断是否为交易日如果已加载
* @param {Date} date
* @returns {boolean}
*/
isTradingDayByCSV(date) {
if (this.tradingDaysSet) {
return this.tradingDaysSet.has(this.formatDate(date));
}
// 如果没有CSV数据回退到默认判断
return this.isTradingDay(date);
}
}
// 导出单例
export const tradingDayUtils = new TradingDayUtils();
export default tradingDayUtils;