Вы можете использовать pandas Grouper , чтобы сгруппировать данные по частоте. Единственным недостатком является то, что у вас не может быть перекрытия windows, и он будет повторяться в течение времени, которое не существует.
Первая проблема в основном означает, что окно будет скользить от 9:30
- 9:59
до 10:00
- 10:29
вместо 9:30
- 10:00
на 10:00
- 10:30
.
Вторая проблема возникает в праздничные дни и ночью, когда торговля не ведется. Следовательно, если у вас большой период без торговли, вы можете разделить DataFrame
и затем объединить их.
Создать пример данных
import pandas as pd
import numpy as np
time = pd.date_range(start="2014-01-02 09:30",
end="2014-01-02 16:00", freq="min")
time = time.append( pd.date_range(start="2014-01-03 09:30",
end="2014-01-03 16:00", freq="min") )
df = pd.DataFrame(data=np.random.rand(time.shape[0], 4)-0.5,
index=time, columns=['SPY','AAPL','AMZN','T'])
определить диапазон, который вы хотите использовать
freq = '30min'
obs_per_day = len(pd.date_range(start = "9:30", end = "16:00", freq = "30min"))
trading_days = len(pd.unique(df.index.date))
создайте функцию для расчета бета-значений
def beta(df):
if df.empty: # returns nan when no trading takes place
return np.nan
mat = df.to_numpy() # numpy is faster than pandas
m = mat.mean(axis=0)
mat = mat - m[np.newaxis,:] # demean
dof = mat.shape[0] - 1 # degree of freedom
if dof != 0: # check if you data has more than one observation
mat = mat.T.dot(mat[:,0]) / dof # covariance with first column
return mat[1:] / mat[0] # beta
else:
return np.zeros(mat.shape[1] - 1) # return zeros for to short data e.g. 16:00
и в конце используйте pd.groupby().apply()
res = df.groupby(pd.Grouper(freq=freq)).apply(beta)
res = np.array( [k for k in res.values if ~np.isnan(k).any()] ) # remove NaN
res = res.reshape([trading_days, obs_per_day, df.shape[1]-1])
Примечание. что результат немного отличается от вашего. Результаты также немного отличаются из-за разного скольжения окон. Чтобы проверить, совпадают ли результаты, просто попробуйте что-нибудь вроде этого
trading_days = pd.unique(df.index.date)
# Your result
moments1 = pd.date_range(start = "9:30", end = "10:00", freq = "30min").time
beta(df[df.index.date == trading_days[0]].between_time(moments1[0], moments1[1]))
# mine
moments2 = pd.date_range(start = "9:30", end = "10:00", freq = "29min").time
beta(df[df.index.date == trading_days[0]].between_time(moments[0], moments2[1]))