在pandas数据框中计算相对成交量比并添加为新列
我知道之前有一些帖子讨论过这个问题,但我的情况有点不同,我想寻求一些帮助。
我有一个名为 symbol_df 的 pandas 数据框,里面有每个股票符号的 1 分钟数据,格式如下:
id Symbol_id Date Open High Low Close Volume
1 1 2023-12-13 09:15:00 4730.95 4744.00 4713.95 4696.40 2300
2 1 2023-12-13 09:16:00 4713.20 4723.70 4717.85 4702.55 1522
3 1 2023-12-13 09:17:00 4716.40 4718.55 4701.00 4701.00 909
4 1 2023-12-13 09:18:00 4700.15 4702.80 4696.70 4696.00 715
5 1 2023-12-13 09:19:00 4696.70 4709.90 4702.00 4696.10 895
... ... ... ... ... ... ... ...
108001 1 2024-03-27 13:44:00 6289.95 6291.95 6289.00 6287.55 989
108002 1 2024-03-27 13:45:00 6288.95 6290.85 6289.00 6287.75 286
108003 1 2024-03-27 13:46:00 6291.25 6293.60 6292.05 6289.10 1433
108004 1 2024-03-27 13:47:00 6295.00 6299.00 6293.20 6293.15 2702
108005 1 2024-03-27 13:48:00 6292.05 6296.55 6291.95 6291.95 983
我想计算“相对成交量比率”指标,并将这个计算出的值作为新列添加到 symbol_df 中,使用滚动计算的方式。
“相对成交量比率”指标的计算方法如下:
今天的成交量会和过去 10 天同一时间段的平均成交量进行比较。为了得到这个比率值,我们只需将“今天截至目前的成交量”除以“过去 10 天同一时间段的平均成交量”。
举个例子,现在的时间是 13:48。
cumulativeVolumeOfToday = 1 分钟数据中从 00:00 到 13:48 的成交量
加起来的总和。
averageVolumeOfPreviousDaysOfSamePeriod = 过去 10 天同一时间段(00:00 - 13:48)的成交量平均值
。
relativeVolumeRatio = CumulativeVolumeOfToday/AvergeVolumeOfPrevious10DaysOfSamePeriod
将这个值作为新列添加到数据框中。
测试案例的样本数据下载:
import yfinance as yf #pip install yfinance
from datetime import datetime
import pandas as pd
symbol_df = yf.download(tickers="AAPL", period="7d", interval="1m")["Volume"]
symbol_df=symbol_df.reset_index(inplace=False)
#symbol_df['Datetime'] = symbol_df['Datetime'].dt.strftime('%Y-%m-%d %H:%M')
symbol_df = symbol_df.rename(columns={'Datetime': 'Date'})
#We can only download 7 days sample data. So 5 days mean for calculations
我该如何在 Pandas 中实现这个?
2 个回答
我希望我理解你的问题没错:
- 计算今天的“交易量”总和(也就是从今天零点到现在的时间)
- 计算过去10天内每天从零点到现在的“交易量”总和的平均值
- 用今天的“交易量”总和去除以过去10天的平均值
import numpy as np
import pandas as pd
from tqdm import tqdm
df = df.set_index("Date")
cache = {}
def get_volume_sum(df, dt):
rv = cache.get(dt, None)
if rv is None:
rv = df.loc[dt.date() : dt, "Volume"].sum()
cache[dt] = rv
return rv
out, N = [], 10
for d in tqdm(df.index):
cumulativeVolumeOfToday = get_volume_sum(df, d)
vals = []
for day in range(N, 0, -1):
dt = d - pd.Timedelta(days=day)
vals.append(get_volume_sum(df, dt))
if not vals:
out.append(np.nan)
else:
AvergeVolumeOfPrevious10DaysOfSamePeriod = np.mean(np.array(vals))
if AvergeVolumeOfPrevious10DaysOfSamePeriod != 0:
out.append(
cumulativeVolumeOfToday / AvergeVolumeOfPrevious10DaysOfSamePeriod
)
else:
out.append(np.nan)
df["relativeVolumeRatio"] = out
print(df.tail(10))
打印结果(在我这台AMD 5700x的机器上处理150,000条记录大约花了21秒):
100%|███████████████████████████████████| 151474/151474 [00:21<00:00, 7136.78it/s]
Symbol_id Open Close Volume relativeVolumeRatio
Date
2024-03-27 13:39:00 1 3520 4340 731 0.993289
2024-03-27 13:40:00 1 4868 4748 131 0.992341
2024-03-27 13:41:00 1 5833 5342 1828 0.992327
2024-03-27 13:42:00 1 4006 3419 1755 0.992667
2024-03-27 13:43:00 1 3465 3827 690 0.992588
2024-03-27 13:44:00 1 5624 4924 2315 0.993013
2024-03-27 13:45:00 1 5012 5321 2961 0.993927
2024-03-27 13:46:00 1 3292 4193 112 0.992635
2024-03-27 13:47:00 1 4823 5520 2098 0.993061
2024-03-27 13:48:00 1 4937 4560 679 0.992526
这个数据框是通过以下方式创建的:
np.random.seed(42)
def generate_df(from_, to_):
dr = pd.date_range(from_, to_, freq="1min")
return pd.DataFrame(
{
"Symbol_id": [1] * len(dr),
"Date": dr,
"Open": np.random.randint(3000, 6000, size=len(dr)),
"Close": np.random.randint(3000, 6000, size=len(dr)),
"Volume": np.random.randint(100, 3000, size=len(dr)),
}
)
df = generate_df("2023-12-13 09:15:00", "2024-03-27 13:48:00")
简要说明
from yfinance import download
# Prepare data similar to the original
symbol_df = (
download(tickers="AAPL", period="7d", interval="1m")
.rename_axis(index='Date')
.reset_index()
)
# Calculate Relative Volume Ratio
volume = symbol_df.set_index('Date')['Volume']
dts = volume.index
cum_volume = volume.groupby(dts.date, sort=False).cumsum()
prev_mean = lambda days: (
cum_volume
.groupby(dts.time, sort=False)
.rolling(days, closed='left')
.mean()
.reset_index(0, drop=True) # drop the level with dts.time
)
rvr = cum_volume / prev_mean(5)
# Assign the output to the initial data
symbol_df = symbol_df.join(rvr.rename('Relative volume ratio'), on='Date')
详细解释
根据提供的信息,你需要对汇总的数据进行几次处理。首先,要对每天的数据进行累积汇总。接着,按照一天中的时间对数据进行一个为期十天的窗口计算平均值。最后,把前面的结果除以后面的结果。
假设你有以下测试数据,其中 "Date"
是一个时间日期类型的列:
from yfinance import download
symbol_df = (
download(tickers="AAPL", period="7d", interval="1m")
.rename_axis(index='Date')
.reset_index()
)
为了计算 相对成交量比 的值,我们将 "Volume"
作为一个单独的序列,并用时间日期 "Date"
作为它的索引:
volume = symbol_df.set_index('Date')['Volume']
dts = volume.index # date-time stamps for convenient grouping
现在我们来创建每天的累积成交量序列。为此,我们按日期(只保留年、月、日,不包括时间)对 volume
进行分组,并对每组应用 cumsum
来计算累积和(使用 sort=False
是为了加快计算速度):
cum_volume = volume.groupby(dts.date, sort=False).cumsum()
为了计算在给定天数内同一时间的累积成交量的平均值,我们按时间(只保留小时和分钟,不包括年、月、日)对 cum_volume
进行分组,并对每组应用滚动计算来获得窗口的平均值。需要注意的是,这里需要源数据按时间日期排序,因为只考虑工作日,不能使用不固定频率的 "10B"
作为 window
值。 为了计算正好是前几天的平均值(不包括当前这一天),我们设置 closed='left'
(具体细节可以查看 DataFrameGroupBy.rolling 文档):
prev_mean = lambda days: (
cum_volume
.groupby(dts.time, sort=False)
.rolling(days, closed='left')
.mean()
.reset_index(0, drop=True)
)
现在进行最后一步,使用5天的窗口:
rvr = cum_volume / prev_mean(5)
比较
与 Andrei Kesely 的解决方案 相比,这个方法在速度上更胜一筹(例如,在 Intel Core i3-2100 上,处理那里提供的数据需要超过1分钟,而使用上面的代码只需300-400毫秒)。计算结果在前10天后的时间戳是相同的。但在开始时,当前面少于10天的数据时,滚动窗口的平均值计算是基于假设总是有10个项目(缺失值被设置为nan)。而在 Kesely 的解决方案中,我们只会对 可用的 累积成交量计算平均值。