3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在 JoinQuant 聚宽平台上,为了保护数据库性能和防止内存溢出,finance.run_query 等查询接口通常会限制单次查询的最大返回行数(如 3000 行或 4000 行)。当您需要获取全量的公募基金历史股票持仓数据时,单次查询往往无法满足需求。
要突破这个限制,获取全部完整的数据,最标准且高效的方法是使用 分页查询(Pagination),即结合 SQLAlchemy 的 limit() 和 offset() 方法进行循环抓取。此外,也可以通过按时间段或基金代码分批查询来实现。
以下是具体的解决方案和代码示例:
limit 和 offset 进行分页查询(推荐)这是最通用的方法。limit(N) 表示每次最多取 N 条数据,offset(M) 表示跳过前 M 条数据。通过在一个 while 循环中不断增加 offset 的值,直到某次查询返回的数据量小于 limit,即可确认数据已全部获取完毕。
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,随着偏移量的增大,数据库查询效率也会降低。更好的做法是将大查询拆分为多个小查询,例如按季度或按单只基金进行循环。
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)} 条持仓数据")
filter(如日期、代码)的情况下直接查询全表,这不仅极慢,而且容易触发平台的资源限制。concat:在循环中拼接 DataFrame 时,使用 pd.concat 放入列表中一次性拼接,或者在循环中逐步拼接(如示例所示,但数据量极大时建议先存入 list,最后统一 concat 以提升性能)。write_file),避免重复抓取。