盘一盘 Python 特别篇 19 - 天数计数|年限

    技术2022-07-10  147

    本文含 9220 字,25 图表截屏

    建议阅读 48 分钟

    本文是 Python 系列的特别篇的第十八篇

    特别篇 1 - PyEcharts TreeMap

    特别篇 2 - 面向对象编程

    特别篇 3 - 两大利「器」

    特别篇 4 - 装饰器

    特别篇 5 - Sklearn 0.22

    特别篇 6 - Jupyter Notebook

    特别篇 7 - 格式化字符串

    特别篇 8 - 正则表达式

    特别篇 9 - 正则表达式实战

    特别篇 10 - 错误类型

    特别篇 11 - 异常处理

    特别篇 12 - Collection

    特别篇 13 - Matplotlib Animation

    特别篇 14 - All 和 Any

    特别篇 15 - 透视表 Pivot Table

    特别篇 16 - 交叉表 Cross Table

    特别篇 17 - DateTime

    特别篇 18 - 时区和夏时令

    特别篇 19 - 天数计数惯例和年限

    在金融产品估值时,细节最多的就是如何生成日期和年限了,最核心的细节如下:

    支付日通常不会落在周末和公共假期上,如落到,那么根据“顺延”或“修正顺延”到后一个工作日,或者“提前”或“修正提前”到前一个工作日。工作日惯例解决这个问题。

    两个日期之间的计数有对相应的惯例,比如“Actual/365”计算的天数就是两个日期之间实际的天数,而30/360“”有着特殊的算法。 日期计数惯例解决这个问题。

    本帖分两章,第一章阐明理论,第二章展示代码。

    1

    理论

    1.1

    工作日惯例

    工作日 (business day) 被认为是每周的正式工作的日子。通常对于大多数国家,工作日就是周一至周五,而且不包括公众假期。对于每个国家,公众假期都可能不同,见下图:

    上图中公众假期有两种表现形式:

    年份用 xxxx (黄色高亮) 标示的指的每年这一天都是公众假期,比如 US 下面 1-Jan-xxxx 就是说每年1月1日都是美国的公众假期。

    年份用具体四个数字标示的指的这个具体的日期是公众假期。

    工作日惯例就是通过一些不同规定把一个非工作日调整到一个工作日 (非周末且非公众假期) 上。

    1.    提前 (preceding)

    提前惯例是将非工作日调整成其前一天工作日。

    例子:2016年12月15日 (星期四) 一个月后是 2017年1月15日 (星期天),调整成前一天工作日是 2017年1月13日 (星期五)。此惯例常用在贷款,因此付款通常定在某一天之前。

    2.    顺延 (following)

    顺延惯例将非工作日调整成其后一天工作日。

    例子:2016年12月15日 (星期四) 一个月后是 2017年1月15日 (星期天),但是是星期天,调整成后一天工作日是 2017年1月16日 (星期一)。

    3.    修正提前 (modified preceding)

    修正提前惯例将非工作日调整成其前一天工作日,但是如果这个工作日落在上个月,那么将非工作日调整成后其一天工作日。

    例子:2016年12月1日 (星期四) 一个月后是 2017年1月1日 (星期天而且是公众假期),调整成前一天工作日是 2016年12月30日 (星期五) ,但是已经跨到上个月,因此调整成后一个工作日 2017年1月2日 (星期一)。

    4.    修正顺延 (modified following)

    修正顺延惯例将非工作日调整成其后一天工作日,但是如果这个工作日落在下个月,那么将非工作日调整成其前一天工作日。

    例子:2017年3月30日 (星期四) 一个月后是 2017年4月30日 (星期天),调整成后一天工作日是 2017年5月1日 (星期一) ,但是已经跨到下个月,因此调整成前一个工作日 2017年4月28日 (星期五)。此惯例常用在利率衍生品上。

    5.    月终 (end of month, EOM)

    月终惯例是当起始日是某一个月的最后工作日 (注意不是日历日),那么调整后的结束日也要是某一个月最后工作日 (注意不是日历日)。

    例子:

    2017年2月28日 (星期二) 一个月后是 2017年3月31日 (星期五)

    2017年3月31日 (星期五) 一个月后是 2017年4月30日 (星期天),不是工作日,4月最后一个工作日是 2017年4月28日 (星期五)

    1.2

    全连接层

    年限是将两个日期间的天数按某种规则转化成年数,而日期计数惯例就是这个规则。定义这两个日期为起始日 (其年月日为 y1, m1, d1) 和结束日 (其年月日为 y2, m2, d2),并解释以下缩写含义:

    ISDA: International Swap Derivative Association, 国际掉期与衍生工具协会

    IMCA: International Capital Market Association, 国际资本市场协会

    ISMA: International Security Management Association, 国际证券管理协会

    下面来看看有哪些常见惯例:

    1.    1/1

    该惯例来自 2006 年 ISDA 文件里面的 4.16(a),年限等于 1

    2.    Act/Act

    这两个惯例来自 2006 年 ISDA 文件里面的 4.16(b),计算年限方法为“非闰年里天数/365 + 闰年里天数/366”。这里的天数计算包括起始日不包括结束日。该惯例也称为 Act/Act ISDA。

    例子:

    起始日 2014年12月30日,结束日 2015年1月2日:年限 = 3/365

    起始日 2015年12月30日,结束日 2016年1月2日:年限 = 2/365 + 1/366

    起始日 2016年12月30日,结束日 2017年1月2日:年限 = 2/366 + 1/365

    3.    Act/Act ICMA

    该惯例来自 2006 年 ISDA 文件里面的 4.16(c),计算年限方法为 1/freq × adj。其中 freq 是每年付息次数,而 adj 是根据第一个或最后一个票息是短票息或长票息决定的 (四种类型,起始短票息,起始长票息,结束短票息和结束长票息)。

    4.    Act/365

    该惯例来自 2006 年 ISDA 文件里面的 4.16(d),计算年限方法为 (d2 – d1)/ 365。这里的天数计算包括起始日和结束日。该惯例也称为 Act/365 F。

    5.    Act/360

    该惯例来自 2006 年 ISDA 文件里面的 4.16(e),计算年限方法为 (d2 – d1)/ 360。这里的天数计算包括起始日和结束日。此惯例通常用在到期日小于一年的货币市场产品 (money market instrument)。

    30/360 组

    以下 6 到 9 的 30/360 组有以下的共同公式 [公式1]

    [360(y2–y1)+30(m2–m1)+(d2–d1)] / 360 

    但是用不同方法来决定 y2, m2, d2,  y1, m1, d1。

    6.    30/360

    该惯例来自 2006 年 ISDA 文件里面的 4.16(f),计算年限方法是用公式1并做以下调整:

    当 D1 是 31,将 D1 变成 30。

    当 D2 是 31并且 D1 是 30 或 31,将 D2 变成 30。

    如果使用月终惯例,

    当 D1 和 D2 都是二月最后一个日历日,将 D1 和 D2 都变成 30。

    当 D1 是二月最后一个日历日,将 D1 变成 30。

    该惯例也称为 bond basis,主要用在美国企业债。

    7.    30E/360

    该惯例来自 2006 年 ISDA 文件里面的 4.16(g),计算年限方法是用公式 1 并做以下调整:

    当 D1 是 31,将 D1 变成 30。

    当 D2 是 31,将 D2 变成 30。

    该惯例也称为 30/360 ICMA, 30S/360, Eurobond basis 和 Special German。

    8.    30E/360 (ISDA)

    该惯例来自 2006 年 ISDA 文件里面的 4.16(h),计算年限方法是用公式1并做以下调整:

    当 D1 是某月最后一个日历日,将 D1 变成 30。

    当 D2 是某月最后一个日历日 (如果是二月 D2 必须是到期日),将 D2 变成30。

    该惯例也称为 30E/360 ISDA 和 German。

    9.    30E+/360

    计算年限方法是用公式1并做以下调整:

    当 D1 是 31,将 D1 变成 30。

    当 D2 是 31,将 D2 变成 1 且 M2 变成 M2+1。

    10.  Bus/252

    年限等于包括起始日不包括结束日之间的工作日天数除以 252。该惯例用于巴西市场。

    下图给出基本日期计数惯例的总结:

    2

    代码

    为了实现日期生成,天数和年限计算,我设计了四大模块分别处理:

    基本功能 (basic functions)

    日历 (calendar)

    工作日惯例 (business day convention)

    天数计数惯例 (day count convention)

    首先引用必要的工具包:

    from datetime import date, datetime, timedelta import collections

    2.1

    基本功能

    Processed: 0.010, SQL: 9