🚀 AI 一键生成 ptrade 策略代码

PTrade 策略性能优化:如何通过 is_dict 参数提升数据 API 速度?

ptrade | 发布时间: 2025/12/5
以下内容由 EasyQuant 生成。

问题描述

我的策略里获取大量历史数据很慢,有什么办法可以提速吗?

解决方案

在PTrade策略中,获取大量历史数据(如get_historyget_price)变慢通常是因为Pandas DataFrame的构建开销以及频繁的网络IO请求导致的。

以下是针对PTrade平台特性的几种核心提速方案:

1. 使用 is_dict=True 参数(最推荐)

PTrade的API(get_historyget_price)默认返回的是 pandas.DataFramepandas.Panel 对象。构建这些对象在数据量大时非常消耗时间。

通过设置 is_dict=True,API将直接返回原生字典(Dictionary)和 NumPy 数组,这可以显著减少内存开销并提高速度。

优化前(较慢):

# 返回 DataFrame,构建速度慢
df = get_history(100, '1d', 'close', g.security, is_dict=False)

优化后(极快):

# 返回 Dict,速度大幅提升
data_dict = get_history(100, '1d', 'close', g.security, is_dict=True)
# 访问数据示例:data_dict['600570.SS']['close'][-1]

2. 批量获取数据(避免循环调用)

很多策略会写成循环遍历股票池来获取数据,这会产生大量的网络交互请求,导致极大的延迟。务必一次性传入整个股票列表

优化前(极慢):

# 错误做法:在循环中请求,如果有100只股票,就发生了100次网络IO
for stock in g.security:
    h = get_history(100, '1d', 'close', stock)
    # 处理逻辑...

优化后(快):

# 正确做法:一次性请求所有股票数据,只发生1次网络IO
# g.security 是一个列表,例如 ['600570.SS', '000001.SZ', ...]
all_history = get_history(100, '1d', 'close', g.security, is_dict=True)

for stock in g.security:
    if stock in all_history:
        data = all_history[stock]
        # 处理逻辑...

3. 将数据获取移至 before_trading_start

如果你的策略逻辑依赖的是日线级别的历史数据,而策略运行在分钟级别或Tick级别,不要在 handle_data 中重复获取历史数据

handle_data 在分钟回测中每分钟运行一次,重复获取昨日及之前的历史数据是巨大的浪费。

优化方案:

def before_trading_start(context, data):
    # 每天开盘前一次性获取历史数据并存储在全局变量 g 中
    # 注意:这里获取的是截止到昨天的历史数据
    g.history_data = get_history(100, '1d', 'close', g.security, is_dict=True)

def handle_data(context, data):
    # 盘中直接使用内存中的数据,无需再次请求API
    for stock in g.security:
        # 获取历史收盘价
        past_closes = g.history_data[stock]['close']
        # 获取当前分钟的最新价
        current_price = data[stock]['close']
        # 结合使用...

4. 减少不必要的字段和数据长度

  • 精简字段 (field):只获取你需要的字段。如果你只需要收盘价,不要请求 ['open', 'high', 'low', 'close', 'volume'],这会减少数据传输量。
  • 精简长度 (count):只获取计算指标所需的最小长度。例如计算MA5,获取10-20根K线即可,不要获取1000根。

5. 综合优化代码示例

以下是一个结合了上述所有优化技巧的策略代码模板:

def initialize(context):
    # 假设股票池有上百只股票
    g.security = get_index_stocks('000300.SS')
    set_universe(g.security)

def before_trading_start(context, data):
    # 优化点1 & 2 & 3:
    # 1. 在盘前统一获取
    # 2. 传入列表批量获取
    # 3. 使用 is_dict=True 避免 DataFrame 开销
    # 4. 只获取需要的 'close' 字段
    g.all_history = get_history(30, '1d', 'close', g.security, is_dict=True)

def handle_data(context, data):
    # 遍历股票池
    for stock in g.security:
        # 容错处理:确保取到了数据
        if stock not in g.all_history:
            continue
            
        # 直接从内存读取 Numpy 数组,速度极快
        hist_closes = g.all_history[stock]['close']
        
        # 计算均线 (使用 Numpy 计算比 Pandas 更快)
        if len(hist_closes) > 20:
            ma20 = hist_closes[-20:].mean()
            
            # 获取当前价格
            current_price = data[stock]['close']
            
            # 简单的交易逻辑
            if current_price > ma20:
                # 这里的 order 只是示例
                pass

Q&A: 常见数据获取问题

Q: 为什么我使用了 is_dict=True 后,代码报错说没有 ilocloc 属性?
A: is_dict=True 返回的是 Python 字典和 NumPy 数组(numpy.ndarray),而不是 Pandas DataFrame。NumPy 数组不支持 ilocloc。你需要使用数组切片方式访问,例如 data[-1] 获取最后一个元素,data[-5:] 获取最后5个元素。

Q: get_historyget_price 可以使用多线程加速吗?
A: 不可以。PTrade 文档明确指出:“该接口与get_price接口不支持多线程同时调用”。在 run_dailyrun_interval 中与 handle_data 并发调用,或者自己开启 Python 线程调用这些接口,会导致数据获取为空或程序异常。请坚持使用上述的“批量获取”方法代替多线程。

Q: 如果我必须使用 DataFrame 进行复杂的 Pandas 计算怎么办?
A: 建议先使用 is_dict=True 快速获取数据,然后在策略本地将需要的少量数据转换为 DataFrame。这样比让 API 在底层构建巨大的 DataFrame 传输过来要快。或者,尝试使用 NumPy 函数重写你的指标计算逻辑,NumPy 通常比 Pandas 更快。