如何使用Pandas更快地计算滚动体积轮廓?

mnemlml8  于 2023-03-16  发布在  其他
关注(0)|答案(1)|浏览(147)

问题

我正在做一些市场分析,作为技术分析的一部分,我使用了一个“滚动”Volume Profile指标,它计算最后x根蜡烛(window)中成交量最大的价格水平,并且计算每根蜡烛。
这会产生一个问题,因为需要处理的 Dataframe 值的大小乘以window。例如,如果 Dataframe 有1000行,并且window设置为10,则必须处理10000行。
我通常使用5 - 30之间的window参数,通常所有 Dataframe 大小为1.5 - 2.5百万行*(因为它太慢了,我无法将窗口设置为大于30)*。
我只需要“控制点”,即window最高音量的价格水平。

以下是图表中的实施情况:

问题

  • 有没有什么方法可以加快这个过程,省去“昂贵的”for循环,并利用pandasnumpy函数?
  • 是否可以比使用pandas.DataFrame.groupby方法更快地对具有相同价格水平的值进行分组?
  • 是否有可能降低120 sec / 1M行的处理时间?

所用技术的选择

  • 巨蟒3.10
  • Pandas1.4.4
  • 麻木1.24.2
  • AWS ECS Fargate中运行(但在本地上的结果非常相似);1个vCPU和8 GB内存 (线程/并行可能不太有效)

算法变量说明

*控制点:一段时间内成交量最高的价格水平
*窗口:用于计算体积曲线的烛光数(最后x烛光)
*精度:我将价格舍入到的价格水平(如果设置为10 =〉13 -> 10;设置为100 =〉18421 -> 18400

当前实施

  • 注意:我已经从代码中删除了一些注解**注意:我理解计算的不准确方式,但我没有刻度数据来进行更精确的计算 *

Dataframe 示例

datetime    close     volume
0  2019-11-05 22:17:00  9311.73  17.535066
1  2019-11-05 22:18:00  9306.09  19.708704
2  2019-11-05 22:19:00  9307.72  17.067911
3  2019-11-05 22:20:00  9303.72  39.139451
4  2019-11-05 22:21:00  9300.23  19.286789
5  2019-11-05 22:22:00  9300.43  49.699525
6  2019-11-05 22:23:00  9299.52   8.923734
7  2019-11-05 22:24:00  9299.82  27.646121
8  2019-11-05 22:25:00  9307.83  14.054908
9  2019-11-05 22:26:00  9314.66  16.678026
10 2019-11-05 22:27:00  9317.17  13.971519
11 2019-11-05 22:28:00  9323.35  30.626054
12 2019-11-05 22:29:00  9323.48  31.870815
13 2019-11-05 22:30:00  9323.41  12.434114
14 2019-11-05 22:31:00  9327.88   6.688897
15 2019-11-05 22:32:00  9332.30   9.494066
16 2019-11-05 22:33:00  9328.17  11.473973
17 2019-11-05 22:34:00  9324.59  16.432248
18 2019-11-05 22:35:00  9319.58  15.865908
19 2019-11-05 22:36:00  9318.79  10.796839

这将定位/计算POC

# Now, create a rolling maximum of last x candles only
def get_poc(input_df,
            precision):
    profile = input_df.groupby('close')[['volume']].sum()

    point_of_control = profile['volume'].idxmax()

    # In order to draw line just in the middle of the bar with the highest
    # value we need to add half of the precision to the value

    point_of_control_middle = point_of_control + (precision / 2)

    return point_of_control_middle

主定义

def generate_vrp(input_df,
                 precision,
                 window):
    logger.info("> Generating volume profiles (VRP)")

    num_rows = input_df.shape[0]

    # Price levels
    price_levels = pd.DataFrame(columns=['datetime', 'vrp_level'])

    input_df['close'] = (input_df['close'] / precision).round() * precision

    # In order to get to the last value, we need to do :"- window + 1"
    for i in tqdm(range(1, num_rows)):

        if i < window:
            rows = input_df[0:i].copy()
        else:
            rows = input_df[i - window:i].copy()

        # Get datetime from last row in the dataset
        last_row = rows.iloc[-1]

        # Get the volume profile
        new_data = pd.DataFrame({'datetime': last_row['datetime'],
                                 'vrp_level': get_poc(rows,
                                                      precision)},
                                index=[0])

        price_levels = pd.concat([price_levels, new_data], ignore_index=True)

    return price_levels

方法调用

window = 30 # 10 candles
precision = 10 # the volume profile "step" is 100
generate_vrp(df, precision, window)

响应示例 (仅列日期时间和vrp_level/poc)

  • 精度:10
  • 窗口:30
datetime  vrp_level
0  2019-11-05 22:17:00     9315.0
1  2019-11-05 22:18:00     9315.0
2  2019-11-05 22:19:00     9315.0
3  2019-11-05 22:20:00     9315.0
4  2019-11-05 22:21:00     9305.0
5  2019-11-05 22:22:00     9305.0
6  2019-11-05 22:23:00     9305.0
7  2019-11-05 22:24:00     9305.0
8  2019-11-05 22:25:00     9305.0
9  2019-11-05 22:26:00     9305.0
10 2019-11-05 22:27:00     9305.0
11 2019-11-05 22:28:00     9305.0
12 2019-11-05 22:29:00     9305.0
13 2019-11-05 22:30:00     9305.0
14 2019-11-05 22:31:00     9305.0
15 2019-11-05 22:32:00     9305.0
16 2019-11-05 22:33:00     9305.0
17 2019-11-05 22:34:00     9305.0
18 2019-11-05 22:35:00     9305.0
19 2019-11-05 22:36:00     9305.0
20 2019-11-05 22:37:00     9325.0

scalene的性能分析显示,潜在问题可能是groupbyget_poc函数。

可能的解决方案/尝试过的方法

  • :尝试;未工作
  • 💡:和我将要测试的想法
  • 😐:未完全解决问题,但有助于解决问题
  • :工作溶液
测试大众/py-market-profile

这个想法是测试py-market-profile而不是get_poc方法,在我的本地测试中,它在体积分布计算中表现出了非常好的性能结果。
此实现后的测试结果不太好。Scalene指出此行是mp_slice = mp[i - window:i]的瓶颈。它比初始解决方案差。

def generate_vrp(df, precision, window):
    num_rows = df.shape[0]

    price_levels = pd.DataFrame(columns=['datetime', 'vrp_level'])
    df['close'] = (df['close'] / precision).round() * precision
    df = df.rename(columns={'close': 'Close',
                            'volume': 'Volume'})
    mp = MarketProfile(df)

    for i in tqdm(range(1, num_rows)):
        if i < window:
            mp_slice = mp[0:i]
        else:
            mp_slice = mp[i - window:i]

        poc = mp_slice.poc_price
        last_row = df.iloc[i]
        new_data = pd.DataFrame({'datetime': last_row['datetime'],
                                 'vrp_level': poc},
                                index=[0])

        price_levels = pd.concat([price_levels, new_data], ignore_index=True)

    return price_levels

解决方案基准测试

  • 使用1 k、10 k、20 k、50 k和100 k Dataframe 行;使用timeit x1c4d 1x测量
  • 即使我没有得到任何答案,我也会继续测试和编辑,直到找到解决方案,并希望能帮助其他人。*
nhaq1z21

nhaq1z211#

我希望我能正确理解你的问题。也许你可以使用pd.DataFrame.rolling()

window = 10 # 10 candles
precision = 100 # the volume profile "step" is 100

df["close"] = (df["close"] / precision).round() * precision

def roll_func(x):
    profile = df.iloc[x.index].groupby("close")["volume"].sum()
    return profile.idxmax()

df['vrp_level_2'] = df.rolling(window, min_periods=1)['volume'].apply(roll_func) + (precision / 2)
print(df)

这将打印:

datetime   close     volume  vrp_level_2
0  2019-11-05 22:17:00  9300.0  17.535066       9350.0
1  2019-11-05 22:18:00  9300.0  19.708704       9350.0
2  2019-11-05 22:19:00  9300.0  17.067911       9350.0
3  2019-11-05 22:20:00  9300.0  39.139451       9350.0
4  2019-11-05 22:21:00  9300.0  19.286789       9350.0
5  2019-11-05 22:22:00  9300.0  49.699525       9350.0
6  2019-11-05 22:23:00  9300.0   8.923734       9350.0
7  2019-11-05 22:24:00  9300.0  27.646121       9350.0
8  2019-11-05 22:25:00  9300.0  14.054908       9350.0
9  2019-11-05 22:26:00  9300.0  16.678026       9350.0
10 2019-11-05 22:27:00  9300.0  13.971519       9350.0
11 2019-11-05 22:28:00  9300.0  30.626054       9350.0
12 2019-11-05 22:29:00  9300.0  31.870815       9350.0
13 2019-11-05 22:30:00  9300.0  12.434114       9350.0
14 2019-11-05 22:31:00  9300.0   6.688897       9350.0
15 2019-11-05 22:32:00  9300.0   9.494066       9350.0
16 2019-11-05 22:33:00  9300.0  11.473973       9350.0
17 2019-11-05 22:34:00  9300.0  16.432248       9350.0
18 2019-11-05 22:35:00  9300.0  15.865908       9350.0
19 2019-11-05 22:36:00  9300.0  10.796839       9350.0

相关问题