交易时间的自定义 pandas 日期时间偏移/频率:9:30AM-4:00PM 营业日,美国假期除外

Custom pandas Datetime offset / frequency for trading times: 9:30AM-4:00PM business days except US holidays

从 9:30AM 开始的非节假日工作日记录交易数据,并定期(5 秒、5 分钟、30 分钟等)记录数据,直到 4:00PM。例如非节假日工作日[9:30AM,9:35AM,...,3:50PM,3:55PM]是有5分钟数据的时间

pandas是否具备正确处理这种时间结构所需的能力?我看到有一些方法可以将 custom holidays 添加到工作日 (9:00AM-4:00PM)。但我还没有看到定义自定义营业时间,比如 (9:30AM-4:00PM)。例如,是否可以定义自定义 交易时间 [9:30AM、10:30AM、... 3:30PM] 或 交易 5 分钟,利用 营业时间?

的任何代码

例如,是否有一种 pandas 驱动的方式来生成 DatetimeIndex 的频率,比方说,遵循交易时间规则的 5 分钟频率:

或者只是目前不支持?

也就是说,methods/classes目前pandas有什么可以实现这种功能?如果有 none,有人知道其他可以提供帮助的图书馆吗?

您可以通过正常方式生成一个 5 分钟的代码,然后使用 DatetimeIndex.indexer_between_time 获取自定义交易时间的 datetimeindex (https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DatetimeIndex.indexer_between_time.html )

示例: 如果我是您创建的日期时间索引,那么下面的代码将为您提供交易时间的自定义日期时间索引

i [i.indexer_between_time('09:30:00', '04:00:00', include_end=False) ]

我有同样的问题,至少是关于

的部分

is there a pandas-powered way to generate a DatetimeIndex of, lets say, 5 minute frequency that follows the rules of trading times?

我在下面有一个答案,但首先,关于:

Does pandas have the capabilities required to correctly work with this sort of time structure?

“一起工作”这个词相当含糊;你想做什么“工作”?
Pandas 绝对可以使用这种时间结构 来做一些事情 但也许不能做其他事情。

关于.

For instance, is it possible to define a custom trading hour [9:30AM, 10:30AM, ... 3:30PM] ... ?

答案是肯定的,但是有一些限制,我将在下面描述。 也就是说,我将主要关注生成遵循交易时间和交易日规则的日期时间索引的能力,并在下面演示如何使用 pandas 执行此操作。首先,我将提及一些现有的 pandas 工具及其局限性。

我昨天晚上的大部分时间都在研究这个。 pandas 必须生成日期时间索引范围的大多数函数都接受输入频率 freq 作为字符串(例如 '15T' for 15分钟,或 'D' 每天)。

这些相同的函数通常也会接受 pandas time series offset object in place of the frequency. There are many different kinds of offsets: Each defines a frequency that can have rules, such as skipping over weekends (for example, offsets.BusinessDay) or generating a datetime index for business hours only,例如仅从上午 9 点到下午 5 点。

我在频率和偏移对象方面遇到的主要问题是(大部分)无法将它们组合起来.

例如,使用 BusinessHour offset class I can specify that I want to generate an index containing only business hours (trading hours), and I can even do so only for business days (over a bunch of dates) but I cannot combine this with specifying a frequency such as once per minute, or once every 15 minutes. Rather, the BusinessHour 偏移量 class 似乎默认为每小时 一次 的频率,我没有办法发现要改。

或者,例如,我可以使用 pandas.bdate_range() 指定频率 '30T' 每 30 分钟生成一个索引点,但将包括非工作日。如果我将频率设置为 'B' 仅适用于工作日,那么它会跳过非工作日,但我每天只能获得一个索引点。我发现没有办法将这两个频率结合起来。

我想出的最简单的解决方案是生成工作日(交易日)列表,然后循环遍历该列表,以所需频率(1 分钟或 5 分钟,或15 分钟)从每个日期的开放时间到关闭时间。然后我使用 pandas DatetimeIndex.union_many() 方法将日期时间索引合并为一个。代码如下所示:

def trading_day_range(bday_start=None,bday_end=None,bday_freq='B',
                      open_time='09:30',close_time='16:00',iday_freq='15T',weekmask=None):

    if bday_start is None: bday_start = pd.Timestamp.today()
    if bday_end   is None: bday_end = bday_start + pd.Timedelta(days=1)

    daily = []
    for d in pd.bdate_range(start=bday_start,end=bday_end,freq=bday_freq,weekmask=weekmask):
        topen  = pd.Timestamp(open_time)
        d1    = d.replace(hour=topen.hour,minute=topen.minute)
        tclose = pd.Timestamp(close_time)
        d2    = d.replace(hour=tclose.hour,minute=tclose.minute+1)
        daily.append(pd.date_range(d1,d2,freq=iday_freq))
   
    index = daily[0].union_many(daily[1:])
    return index

以下是一些使用示例:

ix = trading_day_range()
print('len(ix)=',len(ix))
print(ix[20:40])
print(ix[-20:])
len(ix)= 54
DatetimeIndex(['2021-04-13 14:30:00', '2021-04-13 14:45:00',
               '2021-04-13 15:00:00', '2021-04-13 15:15:00',
               '2021-04-13 15:30:00', '2021-04-13 15:45:00',
               '2021-04-13 16:00:00', '2021-04-14 09:30:00',
               '2021-04-14 09:45:00', '2021-04-14 10:00:00',
               '2021-04-14 10:15:00', '2021-04-14 10:30:00',
               '2021-04-14 10:45:00', '2021-04-14 11:00:00',
               '2021-04-14 11:15:00', '2021-04-14 11:30:00',
               '2021-04-14 11:45:00', '2021-04-14 12:00:00',
               '2021-04-14 12:15:00', '2021-04-14 12:30:00'],
              dtype='datetime64[ns]', freq=None)
DatetimeIndex(['2021-04-14 11:15:00', '2021-04-14 11:30:00',
               '2021-04-14 11:45:00', '2021-04-14 12:00:00',
               '2021-04-14 12:15:00', '2021-04-14 12:30:00',
               '2021-04-14 12:45:00', '2021-04-14 13:00:00',
               '2021-04-14 13:15:00', '2021-04-14 13:30:00',
               '2021-04-14 13:45:00', '2021-04-14 14:00:00',
               '2021-04-14 14:15:00', '2021-04-14 14:30:00',
               '2021-04-14 14:45:00', '2021-04-14 15:00:00',
               '2021-04-14 15:15:00', '2021-04-14 15:30:00',
               '2021-04-14 15:45:00', '2021-04-14 16:00:00'],
              dtype='datetime64[ns]', freq=None)
ix1 = trading_day_range('01/01/2021 09:30','01/13/2021 16:00',
                         bday_freq='C',iday_freq='30T',weekmask='Wed Thu Fri')
print('len(ix1)=',len(ix1))
print(ix1[20:40])
print(ix1[-20:])
len(ix1)= 70
DatetimeIndex(['2021-01-06 12:30:00', '2021-01-06 13:00:00',
               '2021-01-06 13:30:00', '2021-01-06 14:00:00',
               '2021-01-06 14:30:00', '2021-01-06 15:00:00',
               '2021-01-06 15:30:00', '2021-01-06 16:00:00',
               '2021-01-07 09:30:00', '2021-01-07 10:00:00',
               '2021-01-07 10:30:00', '2021-01-07 11:00:00',
               '2021-01-07 11:30:00', '2021-01-07 12:00:00',
               '2021-01-07 12:30:00', '2021-01-07 13:00:00',
               '2021-01-07 13:30:00', '2021-01-07 14:00:00',
               '2021-01-07 14:30:00', '2021-01-07 15:00:00'],
              dtype='datetime64[ns]', freq=None)
DatetimeIndex(['2021-01-08 13:30:00', '2021-01-08 14:00:00',
               '2021-01-08 14:30:00', '2021-01-08 15:00:00',
               '2021-01-08 15:30:00', '2021-01-08 16:00:00',
               '2021-01-13 09:30:00', '2021-01-13 10:00:00',
               '2021-01-13 10:30:00', '2021-01-13 11:00:00',
               '2021-01-13 11:30:00', '2021-01-13 12:00:00',
               '2021-01-13 12:30:00', '2021-01-13 13:00:00',
               '2021-01-13 13:30:00', '2021-01-13 14:00:00',
               '2021-01-13 14:30:00', '2021-01-13 15:00:00',
               '2021-01-13 15:30:00', '2021-01-13 16:00:00'],
              dtype='datetime64[ns]', freq=None)