问题描述
我目前用的是
def filter_limitup_stock_pd(context, stock_list):
last_prices = history(1, unit='1m', field='close', security_list=stock_list, df=True)
high_limit = history(1, unit='1m', field='high_limit', security_list=stock_list, df=True)
data = high_limit - last_prices
data = data[data>0]
return data
这种写法,但是性能不佳,看 profile 两次 history 开销很大
解决方案
在 JoinQuant 聚宽平台中,使用 history 函数来获取当前价格和涨停价确实会导致较大的性能开销。原因在于 history 主要用于获取历史时间序列数据,并且默认返回 pandas.DataFrame 格式,底层的数据拼接和 DataFrame 对象的创建非常耗时。
性能最佳的替代方案:使用 get_current_data()
聚宽专门提供了 get_current_data() API 来获取当前时刻的快照数据(包括最新价、涨跌停价、停牌状态等)。这个函数返回的是一个按需加载的字典,底层经过了高度优化,开销极小。
你可以将你的过滤函数优化为如下形式:
def filter_limitup_stock(context, stock_list):
# 获取当前时刻的数据快照
current_data = get_current_data()
# 使用列表推导式进行高效过滤
# 如果最新价严格小于涨停价,则说明未涨停
filtered_stocks = [
stock for stock in stock_list
if current_data[stock].last_price < current_data[stock].high_limit
]
return filtered_stocks
为什么这种方式性能更好?
- 按需获取 (Lazy Loading):
get_current_data()返回的字典初始是空的,只有当你通过current_data[stock]访问特定股票时,引擎才会去内存中提取该股票的数据,避免了全市场数据的无谓加载。 - 避免 DataFrame 开销:原方法中
history(..., df=True)会在底层构建 Pandas DataFrame,这在 Python 中是一个相对沉重的操作。新方法直接操作对象属性,速度快了几个数量级。 - 单次调用:原方法调用了两次
history,而新方法只需调用一次get_current_data()即可同时拿到last_price和high_limit。
进阶提示
如果你还需要同时过滤掉停牌和跌停的股票,可以顺便在这个推导式中一并完成,效率极高:
def filter_untradable_stocks(context, stock_list):
current_data = get_current_data()
return [
stock for stock in stock_list
if not current_data[stock].paused # 未停牌
and current_data[stock].last_price < current_data[stock].high_limit # 未涨停
and current_data[stock].last_price > current_data[stock].low_limit # 未跌停
]
建议在策略中全面使用 get_current_data() 来替代 history 获取当天的截面状态数据,你的策略运行速度(尤其是分钟级或 Tick 级回测)将会有显著提升。