问题描述
解决方案
是的,history 函数可以通过设置参数直接返回字典(包含 numpy.ndarray),从而避免创建 DataFrame 带来的性能开销。
在 JoinQuant API 中,pandas.DataFrame 的创建和操作相对较慢。如果您的策略需要频繁调用 history 获取数据,建议将 df 参数设置为 False。
解决方法
在调用 history 函数时,添加参数 df=False。
- 默认情况 (
df=True):返回pandas.DataFrame,索引为时间,列为股票代码。 - 优化后 (
df=False):返回一个 字典 (dict)。- Key:股票代码(str)。
- Value:
numpy.ndarray数组,对应请求的字段数据。
代码示例
以下代码展示了如何使用 df=False 以及如何访问返回的数据:
# -*- coding: utf-8 -*-
def handle_data(context, data):
# 设定要获取的股票列表
security_list = ['000001.XSHE', '000002.XSHE']
# 1. 获取数据:设置 df=False
# 获取过去5天的收盘价,返回字典格式
h = history(5, unit='1d', field='close', security_list=security_list, df=False)
# 2. 访问数据
# h 的结构类似于:
# {
# '000001.XSHE': array([10.1, 10.2, 10.1, 10.3, 10.4]),
# '000002.XSHE': array([8.5, 8.6, 8.5, 8.7, 8.8])
# }
for stock in security_list:
# 获取该股票对应的 numpy 数组
prices = h[stock]
# 获取最新一个收盘价 (数组的最后一个元素)
current_close = prices[-1]
# 计算平均值 (使用 numpy 的方法,而不是 pandas 的 .mean())
avg_price = prices.mean()
# 打印结果
print("股票: %s, 最新价: %f, 均价: %f" % (stock, current_close, avg_price))
注意事项
- 方法变更:当
df=False时,返回的是numpy数组,因此不能使用 pandas 的特有方法(如.iloc,.loc,.rolling()等)。您需要使用 Python 原生的列表切片(如[-1])或numpy的计算函数(如.mean(),.sum())。 - attribute_history:同样的优化也适用于
attribute_history函数,它也支持df=False参数。 - get_bars:如果您需要获取更大量的数据或更灵活的字段,也可以考虑使用
get_bars,它默认返回numpy结构(当df=False时),通常比history更高效。
Q&A
Q: 为什么 DataFrame 会比 numpy 数组慢?
A: Pandas DataFrame 是建立在 Numpy 之上的高级数据结构,提供了丰富的索引和数据对齐功能。在创建 DataFrame 时,系统需要进行额外的索引构建、数据校验和内存分配,这在循环高频调用时会产生显著的累积开销。而 Numpy 数组是底层的数值矩阵,结构简单,内存连续,计算效率极高。
Q: attribute_history 函数也可以这样优化吗?
A: 是的。attribute_history(security, count, unit='1d', fields=..., df=False) 同样支持该参数。当 df=False 时,它返回一个字典,Key 是字段名(如 'open', 'close'),Value 是 Numpy 数组。
Q: 如果我必须使用 DataFrame 的某些功能(如 rolling),该怎么办?
A: 如果必须使用 DataFrame 的特定功能,建议尽量减少调用 history 的次数。例如,不要在循环内部反复获取数据,而是一次性获取所有股票的数据(history 支持传入股票列表),然后在循环外处理,或者仅在必要计算指标时才转换为 DataFrame。
Q: get_bars 和 history 在速度上有什么区别?
A: get_bars 是较新的 API,设计上更高效。它默认返回 numpy array (当 df=False)。对于获取大量历史数据,get_bars 通常比 history 更快,且支持获取包含当前时刻的数据 (include_now 参数)。