在pandas数据框中计算相对成交量比并添加为新列

2 投票
2 回答
95 浏览
提问于 2025-04-12 14:29

我知道之前有一些帖子讨论过这个问题,但我的情况有点不同,我想寻求一些帮助。

我有一个名为 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 个回答

1

我希望我理解你的问题没错:

  1. 计算今天的“交易量”总和(也就是从今天零点到现在的时间)
  2. 计算过去10天内每天从零点到现在的“交易量”总和的平均值
  3. 用今天的“交易量”总和去除以过去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")
1

简要说明

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 的解决方案中,我们只会对 可用的 累积成交量计算平均值。

撰写回答