背景
最近在研究期权期货,有个 2 个关键指标「合约月份」和「交割日期」很常用,但是难获取。它们的计算逻辑其实挺清楚的,但是就是没找到现成的库(如果有知道这方面库的同学请留言推荐),所以自己写了一个函数。
正文
规则
我个人关注的主要有 3 个期权/期货产品:ETF 期权、股指期权、股指期货。他们的规则可以在 中国金融期货交易所网站 上查得到。规则如下:
产品 | 合约月份 | 交割日期 |
---|---|---|
ETF 期权 | 当月、下月及随后两个季月 | 合约到期月份的第四个周三,遇国家法定假日顺延 |
股指期权 | 当月、下2个月及随后3个季月 | 合约到期月份的第三个周五,遇国家法定假日顺延 |
股指期货 | 当月、下月及随后两个季月 | 合约到期月份的第三个周五,遇国家法定假日顺延 |
获取某月第 n 个星期 m
先来解决这个问题,直接上代码:
// 获取某几月第n个星期day
export const getNthDayOfMonths = (yearMonths: string[], n = 3, day = 5) =>
yearMonths.map((ym) => {
const firstDayOfMonth = moment(ym);
let diff = day - 7 - firstDayOfMonth.day();
if (firstDayOfMonth.day() > day) {
diff += 7;
}
return firstDayOfMonth.add(7 * n + diff, 'days').format('YYYY-MM-DD');
});
关键点:
- 入参要支持多个月份的计算;
- 判断当月第一天是否在星期 m 之后,分情况处理要加的天数 diff;
- 此方法没有考虑法定节假日,能加是能加,成本稍微有点大。有兴趣的同学可以移步 《如何判断当天是否是交易日(工作日)》 来查看判断节假日的逻辑。
获取合约月份
有了上面的方法,好多地方就好写了,先上代码:
export enum TradeMonthType {
ETFOp, // ETF 期权
IndexOp, // 股指期权
IndexFeat, // 股指期货
}
// ETF 期权、股指期权、股指期货交易月份及交割日
export const getDealMonthsAndDates = (
type = TradeMonthType.ETFOp,
startDate?: string
) => {
const currDate = moment(startDate);
// 初始化参数
let nextMonthCount = 1;
let nextQuaterCount = 2;
let dealDateWeek = 4;
let dealDateDay = 3;
// 根据 type 重置参数
if (type === TradeMonthType.IndexOp) {
nextMonthCount = 2;
nextQuaterCount = 3;
dealDateWeek = 3;
dealDateDay = 5;
}
if (type === TradeMonthType.IndexFeat) {
dealDateWeek = 3;
dealDateDay = 5;
}
// 判断起始月份
const [currMonthDealDate] = getNthDayOfMonths(
[currDate.format('YYYY-MM')],
dealDateWeek,
dealDateDay
);
if (moment().isSameOrAfter(moment(currMonthDealDate).startOf('D'))) {
currDate.add(1, 'M');
}
// 处理月合约
const monthSet = new Set([currDate.format('YYYYMM')]);
for (let i = 0; i < nextMonthCount; i++) {
monthSet.add(currDate.add(1, 'M').format('YYYYMM'));
}
// 处理季合约
let currQuarterEnd = currDate.clone().endOf('quarter');
if (currDate.month() !== currQuarterEnd.month()) {
currQuarterEnd.add(-1, 'Q');
}
for (let i = 0; i < nextQuaterCount; i++) {
monthSet.add(currQuarterEnd.add(1, 'Q').format('YYYYMM'));
}
const months = Array.from(monthSet);
return {
type,
months,
dealDates: getNthDayOfMonths(months, dealDateWeek, dealDateDay),
};
};
关键点:
- 肯定要根据规则分 type 判断,处理一些初始化的参数,如:第
dealDateWeek
个星期dealDateDay
,下nextMonthCount
个月和nextQuaterCount
个季月; - 要判断当前时间是否已经过了交割日,来确定起始月份是当月还是下月;
- 还要对季月的逻辑进行额外判断,最后保险起见去个重。
总结
这个功能比较常用,在一些非严肃的场景还是可以用一用的,比如查看数据什么的。但是在一些严肃场景,比如实盘交易,最好还是获取软件或交易所提供的 API 数据。
发到这里也是为了记录下,省的这代码后面找不到了。