aboutsummaryrefslogtreecommitdiffstats
path: root/python/dates.py
blob: f45aac09c54825d3d0021d6ff4ab16b77074a5cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import datetime
import pandas as pd
import unittest

from dateutil.relativedelta import relativedelta, WE, MO

try:
    from dateutil.parser import isoparse
except ImportError:
    from dateutil.parser import parse as isoparse

from pandas.tseries.offsets import CustomBusinessDay, Day, QuarterBegin
from pandas.tseries.holiday import get_calendar, HolidayCalendarFactory, GoodFriday

fed_cal = get_calendar("USFederalHolidayCalendar")
bond_cal = HolidayCalendarFactory("BondCalendar", fed_cal, GoodFriday)
bus_day = CustomBusinessDay(calendar=bond_cal())


def imm_dates(start_date, end_date):
    start_date = bus_day.rollback(start_date) - 19 * Day()
    start_date = QuarterBegin(startingMonth=3).rollback(start_date)
    # should be close=left I think
    return (
        pd.date_range(start_date, end_date, freq="QS-MAR", closed=None)
        .shift(19, freq="D")
        .shift(0, bus_day)
    )


def previous_twentieth(d):
    r = datetime.date(d.year, d.month, 20)
    if r > d:
        r -= relativedelta(months=1)
    mod = r.month % 3
    if mod != 0:
        r -= relativedelta(months=mod)
    return r


def imm_date(d):
    if isinstance(d, str):
        d = isoparse(d)
    r = d + relativedelta(day=1, weekday=WE(3))
    if r <= d:
        return imm_date(r + relativedelta(months=1, day=1))
    return r


def days_accrued(tradedate):
    tradedate = pd.Timestamp(tradedate)
    start_protection = tradedate + Day()
    # we want the the largest twentieth imm date rolled forward such that
    # it's less than start_protection
    effective_date = pd.Timestamp(
        previous_twentieth(bus_day.rollback(start_protection).date())
    )
    effective_date = bus_day.rollforward(effective_date)
    delta = start_protection - effective_date
    return delta.days


def isleapyear(date):
    return (date.year % 4) == 0 & (~(date.year % 100 == 0) | date.year % 400 == 0)


def yearfrac(date1, date2, daycount):
    if daycount == "30/360":
        d1 = date1.dt.day.copy()
        d2 = date2.dt.day.copy()
        d2[(d2 == 31) & (d1.isin([30, 31]))] = 30
        d1.replace({31: 30})
        y1 = date1.dt.year
        y2 = date2.dt.year
        m1 = date1.dt.month
        m2 = date2.dt.month
        return (360 * (y2 - y1) + 30 * (m2 - m1) + d2 - d1) / 360
    days_accrued = date2 - date1
    if daycount == "ACT/365":
        return days_accrued.dt.days / 365
    if daycount == "ACT/360":
        return days_accrued.dt.days / 360