Java获取股市交易日

该博客介绍了如何使用Java获取最近及未来的交易日。针对获取最近交易日的需求,考虑到股市交易时间及法定节假日,通过维护数据库中的holiday表来排除非交易日。对于获取第N个交易日,采用递归方式可能导致效率低下,故需要优化算法,避免重复调用。文章提供了部分伪代码作为实现思路的示例。

一、获取最近交易日

为了方便,我将代码写在一个类下,以供参考:

package  test;

import  java.text.SimpleDateFormat;
import  java.util.Arrays;
import  java.util.Calendar;
import  java.util.Date;
import  java.util.List;

public  class  Test {

    public  static  SimpleDateFormat sdf = new  SimpleDateFormat("yyyyMMdd");

    public  static  void  main(String[] args) {
        Calendar cal = Calendar.getInstance();
        // 待确认交易日:15点之前取当前日期,15点之后取下一交易日
        String nextDay = sdf.format(new Date());
        if (cal.get(Calendar.HOUR_OF_DAY) >= 15) {
            if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) {
                cal.add(Calendar.DAY_OF_MONTH, 3);
                nextDay = sdf.format(cal.getTime());
            } else {
                nextDay = getNextDay(nextDay);
            }
        }

        nextDay = "20150930";
        // 时间必须要从小到大排序,否则会出问题
        String[] a = {"20150930", "20151002", "20151003", "20151005", "20151006"};
        List<String> holidayList = Arrays.asList(a);
        System.out.println("有效的最近交易日nextDay:"  + getDayAfterHoliday(nextDay, holidayList));
    }

    public  static  String getDayAfterHoliday(String nextDay, List<String> holidayList) {
        if  (null  != holidayList) {
            for  (int  i = 0; i < holidayList.size(); i++) {
                if  (holidayList.get(i).equals(nextDay)) {
                    nextDay = getDayAfterHoliday(getNextDay(nextDay), holidayList);
                }
            }
        }
        nextDay = getDayExceptWeekend(nextDay);
        return  nextDay;
    }

    /**
     * 获取下一天
     * 
     * @param day 当前日期yyyyMMdd
     * @return
     */
    public  static  String getNextDay(String day) {
        Calendar cal = formatYYYYMMDD(day);
        cal.add(Calendar.DAY_OF_MONTH, 1);
        return  sdf.format(cal.getTime());
    }

    /**
     * 如果当前日期处于周末,则返回下周一
     * 
     * @param day 当前日期yyyyMMdd
     * @return
     */
    public  static  String getDayExceptWeekend(String day) {
        Calendar cal = formatYYYYMMDD(day);
        if  (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
            cal.add(Calendar.DAY_OF_MONTH, 2);
        } else  if  (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
            cal.add(Calendar.DAY_OF_MONTH, 1);
        }
        return  sdf.format(cal.getTime());
    }

    /**
     * 将字符串类型日期转换为Calendar
     * 
     * @param day 当前日期yyyyMMdd
     * @return
     */
    public  static  Calendar formatYYYYMMDD(String day) {
        Calendar cal = Calendar.getInstance();
        cal.set(Integer.parseInt(day.substring(0, 4)), (Integer.parseInt(day.substring(4, 6)) - 1), Integer.parseInt(day.substring(6)));
        return  cal;
    }
}

下面稍微讲解一下实际项目中大致的思路。

1、需求:

获取最近交易日。根据当前时间,如果当天为交易日且股市还未收盘,则显示为当天,否则获取下一交易日。

2、已知:

法定交易时间为9:00 - 11:30和13:00-15:00
国内的股市交易日不包括国家法定假日和周六周日。
国家法定假日不会在年初就确定下来,所以法定假日的数据需要手动维护。

3、简单分析:

首先, 法定假日的数据要手动维护,那么可以在数据库(此例使用MYSQL)专门建一张表holiday来维护这些假日数据。考虑到周末可以通过程序来判断,所以就不准备存储在表中,这样也减少了holiday表的管理工作。

DROP  TABLE  IF EXISTS `holiday`;
CREATE  TABLE  `holiday` (
   `id`  int (11)  NOT  NULL  AUTO_INCREMENT,
   `yyyymmdd`  varchar (8)  NOT  NULL  COMMENT  '年月日(格式20151001)' ,
   `description`  varchar (255)  DEFAULT  NULL ,
   `status`  varchar (1)  NOT  NULL  DEFAULT  '1'  COMMENT  '0有效 1被禁用' ,
   `create_time`  varchar (30)  DEFAULT  NULL ,
   `modify_time`  varchar (30)  DEFAULT  NULL ,
   `create_by`  varchar (100)  DEFAULT  NULL ,
   `modify_by`  varchar (100)  DEFAULT  NULL ,
   PRIMARY  KEY  (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16  DEFAULT  CHARSET=utf8 COMMENT= '法定节日' ;

这时这个假日数据可能需要一个后台管理系统的页面来维护,这里不做讨论,先手动插入几条数据。

INSERT  INTO  `holiday`  VALUES  ( '5' ,  '20151001' ,  '国庆节放假1' ,  '0' ,  '2015-09-17 10:40:24' ,  '2015-09-17 10:57:12' ,  'admin' ,  'admin' );
INSERT  INTO  `holiday`  VALUES  ( '6' ,  '20151002' ,  '国庆节放假2' ,  '0' ,  '2015-09-17 10:40:24' ,  '2015-09-17 10:59:04' ,  'admin' ,  'admin' );
INSERT  INTO  `holiday`  VALUES  ( '7' ,  '20151003' ,  '国庆节放假3' ,  '0' ,  '2015-09-17 10:40:24' ,  '2015-09-17 11:03:07' ,  'admin' ,  'admin' );
INSERT  INTO  `holiday`  VALUES  ( '8' ,  '20151004' ,  '国庆节放假' ,  '0' ,  '2015-09-17 10:41:29' ,  null ,  'admin' ,  null );
INSERT  INTO  `holiday`  VALUES  ( '9' ,  '20151005' ,  '国庆节放假' ,  '0' ,  '2015-09-17 10:41:37' ,  null ,  'admin' ,  null );
INSERT  INTO  `holiday`  VALUES  ( '11' ,  '20151006' ,  '国庆节放假' ,  '0' ,  '2015-09-17 10:42:11' ,  null ,  'admin' ,  null );
INSERT  INTO  `holiday`  VALUES  ( '12' ,  '20151007' ,  '国庆节放假' ,  '0' ,  '2015-09-17 10:42:19' ,  null ,  'admin' ,  null );

注:表里面只存假期,不需要保存周六周日的时间,因为程序里面进行了判断。

二、获取接下来的第N个交易日(N为整数,允许正负)

以上的解决思路解决了获取下一个交易日,同时还用到了递归,这对于程序来说是不好的习惯,因为会严重拖慢程序执行效率。
这时如果需求变动成获取接下来的第N个交易日,为了实现程序的复用,那么就要多次调用这个获取下一个交易日的方法,这就会使得程序效率成倍的降低。所以需要改变一下思路。

需求:

1.获取最近交易日。根据当前时间,如果当天为交易日且股市还未收盘,则显示为当天,否则获取下一交易日。
2.支持获取上一交易日和下一交易日

分析:

1.假设当前日期为D;
2.如果需要获取接下来的第5个交易日,那么如果把当前时间加上5天,也就是(D + 5);
3.这个(D + 5)不一定是我们的真实答案,这5天中间有周末的同时可能还有国家规定的一些节日假期;
4.这时我们需要找出这中间假期的天数M1,然后算出(D + 5 + M1);
5.但是从(D + 5)到(D + 5 + M1)中又有可能存在假期,所以需要再次找出假期天数M2……
最后,当(D + 5 + M1 + … + Mn)到(D + 5 + M1 + … + Mn + Mn1)之间的假期天数Mn1为零时,(D + 5 + M1 + … + Mn + Mn1)即为结果。

这里可以看到,取假期天数成了问题的关键。仔细考虑了下,准备给holiday表把周末的日期也加入,从而减少程序的复杂性。
先上代码(考虑到MVC编程繁复的结构,这里只展示下Service的代码,算是伪代码吧):

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.dao.HolidayDao;
import com.utils.DateUtils;
import com.utils.AndCondition;

@Service
public class HolidayService {

    @Autowired
    private HolidayDao holidayDao;

    /**
     * 获得自startDate开始的第days个交易日(下一交易日即days为1,上一交易日为-1)
     * 
     * @param startDate
     * @param days
     * @return
     */
    public Date getTradingDate(Date startDate, int days) {
        Date tradingDate = startDate;
        if (0 == days) {
            AndCondition andCondition = new AndCondition();
            andCondition.put("yyyymmdd", "=", tradingDate);
            if (0 < holidayDao.countHoliday(andCondition)) {
                // 判断当天是否为交易日,不是则取下一交易日,未考虑取历史交易日
                getTradingDate(tradingDate, 1);
            }
        } else {
            // 上days交易日或下days交易日
            days = Math.abs(days);
            while (days > 0) {
                tradingDate = DateUtils.add(startDate, days, Calendar.DATE);
                days = getHolidaysNum(startDate, tradingDate);
                startDate = tradingDate;
            }
        }
        return tradingDate;
    }

    /**
     * 获得startDate至endDate之间的假期数
     * 
     * @param startDate
     * @param endDate
     * @return
     */
    private int getHolidaysNum(Date startDate, Date endDate) {
        AndCondition andCondition = new AndCondition();
        if (DateUtils.compare(startDate, endDate) < 0) {
            // startDate < endDate
            andCondition.put("yyyymmdd", ">", startDate);
            andCondition.put("yyyymmdd", "<=", endDate);
        } else {
            andCondition.put("yyyymmdd", ">=", endDate);
            andCondition.put("yyyymmdd", "<", startDate);
        }
        return holidayDao.countHoliday(andCondition);
    }
}

这段代码有一个小遗憾,就是如果获取最近的交易日不能取历史的交易日,而是只能取当日或下一交易日。

****************************************END****************************************

评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值