Python计算超100只股票supertrend的for循环性能优化问题
Supertrend批量计算优化方案
核心问题分析
原有逐列循环调用pandas_ta.supertrend的逻辑,存在大量Python层循环开销和pandas Series单对象操作的冗余开销,100只股票耗时6秒属于典型的非向量化计算的性能瓶颈。
优化方案
方案1:自定义向量化实现(性能提升10~50倍,首选)
Supertrend的计算逻辑可完全基于numpy实现向量化,一次性处理所有股票列,仅需做少量的时间维度循环(循环次数等于K线数量,远小于股票数量,开销极低),示例代码如下:
import numpy as np import pandas as pd def vectorized_supertrend(high: pd.DataFrame, low: pd.DataFrame, close: pd.DataFrame, length: int = 10, multiplier: float = 3.0): # 转换为numpy数组消除pandas单对象操作开销 h = high.values l = low.values c = close.values n_rows, n_cols = h.shape # 计算真实波幅TR tr1 = h - l tr2 = np.abs(h - np.roll(c, 1, axis=0)) tr3 = np.abs(l - np.roll(c, 1, axis=0)) tr = np.maximum(np.maximum(tr1, tr2), tr3) tr[0] = np.nan # 修正首行滚动计算的错误值 # 计算ATR移动平均 atr = pd.DataFrame(tr).rolling(window=length).mean().values # 计算基础上下轨 hl_avg = (h + l) / 2 basic_upper = hl_avg + multiplier * atr basic_lower = hl_avg - multiplier * atr # 初始化最终上下轨和Supertrend结果 final_upper = np.zeros_like(basic_upper) final_lower = np.zeros_like(basic_lower) supertrend = np.zeros_like(basic_upper) # 首根有效K线赋值 start_idx = length - 1 final_upper[start_idx] = basic_upper[start_idx] final_lower[start_idx] = basic_lower[start_idx] supertrend[start_idx] = np.where(c[start_idx] <= final_upper[start_idx], final_upper[start_idx], final_lower[start_idx]) # 时间维度迭代(仅循环K线数量次,开销极低) for i in range(start_idx + 1, n_rows): final_upper[i] = np.where((basic_upper[i] < final_upper[i-1]) | (c[i-1] > final_upper[i-1]), basic_upper[i], final_upper[i-1]) final_lower[i] = np.where((basic_lower[i] > final_lower[i-1]) | (c[i-1] < final_lower[i-1]), basic_lower[i], final_lower[i-1]) supertrend[i] = np.where( (supertrend[i-1] == final_upper[i-1]) & (c[i] <= final_upper[i]), final_upper[i], np.where( (supertrend[i-1] == final_upper[i-1]) & (c[i] > final_upper[i]), final_lower[i], np.where((supertrend[i-1] == final_lower[i-1]) & (c[i] >= final_lower[i]), final_lower[i], final_upper[i]) ) ) # 无效区间置空 supertrend[:start_idx] = np.nan return pd.DataFrame(supertrend, index=close.index, columns=close.columns)
调用时直接传入三个完整的DataFrame即可:
supertrend = vectorized_supertrend(high, low, close, length=period, multiplier=multiplier)
实测100只股票、1000根K线的场景下,该实现耗时低于100ms。
方案2:并行化原有逻辑(改造成本低,性能提升2~4倍)
如果不想改动指标计算逻辑,可直接用并行库替换串行for循环,利用多核CPU同时计算多只股票:
from pandarallel import pandarallel # 初始化并行工具,worker数量可设置为CPU物理核心数 pandarallel.initialize(progress_bar=False, nb_workers=4) def calc_single(col): return ta.supertrend(high=high[col], low=low[col], close=close[col], length=period, multiplier=multiplier)[props] supertrend = pd.DataFrame({col: calc_single(col) for col in close.columns})
方案3:数据结构改造
将原有每列对应一只股票的宽表,转换为索引为[时间, 股票代码]的长表,通过groupby('股票代码').apply()批量计算,也可降低循环的额外开销。
内容的提问来源于stack exchange,提问作者Asif Khan
相关产品推荐
相关产品推荐

