🚀 AI 一键生成 joinquant 策略代码
立即体验
👉 AI 生成代码 👈

JoinQuant 聚宽获取完整公募基金历史股票持仓数据指南(突破 3000 行限制)

joinquant | 发布时间: 2026/6/5
以下内容由 EasyQuant 生成。

问题描述

关于基金持股信息数据的请教

目前有一个需求,需要获取公募基金的历史股票持仓数据,根据搜索社区的文章,找到一个连接:https://www.joinquant.com/help/api/help#OTCfund:获取基金持股信息

Img
通过以上介绍可以获取到需要的基金持股信息。

但是根据文档说明:“为了防止返回数据量过大, 我们每次最多返回3000行”

由于限制了获取到的数据量,导致需要的信息不全,请问大家如何才能获取到全部完整的数据?还请不吝赐教,感谢大家!

解决方案

在 JoinQuant 聚宽平台上,为了保护数据库性能和防止内存溢出,finance.run_query 等查询接口通常会限制单次查询的最大返回行数(如 3000 行或 4000 行)。当您需要获取全量的公募基金历史股票持仓数据时,单次查询往往无法满足需求。

要突破这个限制,获取全部完整的数据,最标准且高效的方法是使用 分页查询(Pagination),即结合 SQLAlchemy 的 limit()offset() 方法进行循环抓取。此外,也可以通过按时间段或基金代码分批查询来实现。

以下是具体的解决方案和代码示例:

方法一:使用 limitoffset 进行分页查询(推荐)

这是最通用的方法。limit(N) 表示每次最多取 N 条数据,offset(M) 表示跳过前 M 条数据。通过在一个 while 循环中不断增加 offset 的值,直到某次查询返回的数据量小于 limit,即可确认数据已全部获取完毕。

Python 代码示例:

import pandas as pd
from jqdata import *

def get_full_fund_portfolio(fund_code=None, start_date='2020-01-01'):
    """
    获取完整的基金持股信息
    :param fund_code: 基金代码,若为 None 则查询所有基金
    :param start_date: 起始发布日期
    """
    all_data = pd.DataFrame()
    limit_num = 3000  # 聚宽单次查询限制,可设为 3000 或 4000
    offset_num = 0
    
    while True:
        # 构建基础查询
        # 注意:此处以 finance.FUND_PORTFOLIO_STOCK 为例,请根据实际表名调整
        q = query(finance.FUND_PORTFOLIO_STOCK).filter(
            finance.FUND_PORTFOLIO_STOCK.pub_date >= start_date
        )
        
        # 如果指定了基金代码,则增加过滤条件
        if fund_code:
            q = q.filter(finance.FUND_PORTFOLIO_STOCK.code == fund_code)
            
        # 添加分页参数
        q = q.limit(limit_num).offset(offset_num)
        
        # 执行查询
        df = finance.run_query(q)
        
        # 如果返回为空,说明数据已取完
        if df.empty:
            break
            
        # 将本次查询结果追加到总数据中
        all_data = pd.concat([all_data, df], ignore_index=True)
        
        # 如果本次返回的数据行数小于 limit_num,说明已经是最后一页
        if len(df) < limit_num:
            break
            
        # 更新 offset,准备获取下一页
        offset_num += limit_num
        
    return all_data

# 使用示例:获取某只基金自 2020 年以来的全部持仓
# df_portfolio = get_full_fund_portfolio(fund_code='000001.OF', start_date='2020-01-01')
# print(df_portfolio)

方法二:按时间段(或基金代码)分批查询

如果您需要获取全市场所有基金多年的持仓数据,数据量会非常庞大。此时,即使使用 offset,随着偏移量的增大,数据库查询效率也会降低。更好的做法是将大查询拆分为多个小查询,例如按季度按单只基金进行循环。

Python 代码示例(按基金代码循环):

import pandas as pd
from jqdata import *

# 假设您已经有了一个需要查询的基金代码列表
fund_list = ['000001.OF', '000011.OF', '000021.OF'] 
all_portfolio = pd.DataFrame()

for fund in fund_list:
    # 针对单只基金,数据量通常不会超过 3000 行,直接查询即可
    q = query(finance.FUND_PORTFOLIO_STOCK).filter(
        finance.FUND_PORTFOLIO_STOCK.code == fund,
        finance.FUND_PORTFOLIO_STOCK.pub_date >= '2015-01-01'
    ).limit(3000)
    
    df = finance.run_query(q)
    if not df.empty:
        all_portfolio = pd.concat([all_portfolio, df], ignore_index=True)

print(f"共获取到 {len(all_portfolio)} 条持仓数据")

💡 核心建议与注意事项:

  1. 避免全表扫描:尽量不要在不加任何 filter(如日期、代码)的情况下直接查询全表,这不仅极慢,而且容易触发平台的资源限制。
  2. 合理使用 concat:在循环中拼接 DataFrame 时,使用 pd.concat 放入列表中一次性拼接,或者在循环中逐步拼接(如示例所示,但数据量极大时建议先存入 list,最后统一 concat 以提升性能)。
  3. 注意 API 限制:虽然可以通过分页获取全部数据,但请注意聚宽平台对每日查询总次数或总数据量的潜在限制,合理安排数据抓取任务,建议将抓取到的历史静态数据保存到本地或聚宽的研究环境中(使用 write_file),避免重复抓取。