问题
我正在做一些市场分析,作为技术分析的一部分,我使用了一个“滚动”Volume Profile指标,它计算最后x根蜡烛(window
)中成交量最大的价格水平,并且计算每根蜡烛。
这会产生一个问题,因为需要处理的 Dataframe 值的大小乘以window
。例如,如果 Dataframe 有1000
行,并且window
设置为10
,则必须处理10000
行。
我通常使用5 - 30之间的window
参数,通常所有 Dataframe 大小为1.5 - 2.5百万行*(因为它太慢了,我无法将窗口设置为大于30)*。
我只需要“控制点”,即window
中最高音量的价格水平。
以下是图表中的实施情况:
问题
- 有没有什么方法可以加快这个过程,省去“昂贵的”
for
循环,并利用pandas
或numpy
函数? - 是否可以比使用
pandas.DataFrame.groupby
方法更快地对具有相同价格水平的值进行分组? - 是否有可能降低
120 sec / 1M
行的处理时间?
所用技术的选择
- 巨蟒
3.10
- Pandas
1.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的性能分析显示,潜在问题可能是groupby
和get_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测量 - 即使我没有得到任何答案,我也会继续测试和编辑,直到找到解决方案,并希望能帮助其他人。*
1条答案
按热度按时间nhaq1z211#
我希望我能正确理解你的问题。也许你可以使用
pd.DataFrame.rolling()
:这将打印: