🚀 AI 一键生成 joinquant 策略代码

如何在 JoinQuant 中提取 ETF 持股明细并按持有金额正确排序?

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

问题描述

请问ETF持股明细如何排序

我想提取所有ETF的持股明细,并按照单只ETF持股明细中持有的个股金额排序,但是用rank或者market_cap,不加limit或者加上,都有很多乱序,或者错误排名。

q=query(finance.FUND_PORTFOLIO_STOCK).filter(finance.FUND_PORTFOLIO_STOCK.code=="%s"%i).order_by(finance.FUND_PORTFOLIO_STOCK.pub_date.desc(),finance.FUND_PORTFOLIO_STOCK.rank).limit(100)

query(),filter().order by() 的逻辑到底如何正确使用呢?才不至于出现乱序

解决方案

为什么会出现乱序或错误排名?

在您的代码中,使用了 order_by(finance.FUND_PORTFOLIO_STOCK.pub_date.desc(), finance.FUND_PORTFOLIO_STOCK.rank).limit(100)。出现乱序的核心原因在于跨期数据混合

当您按 pub_date 降序排列并 limit(100) 时,如果该 ETF 最新一期公布的持股明细不足 100 只(例如只公布了前十大重仓股),数据库为了凑够 100 条数据,会自动把上一期、甚至上上一期的数据也拉取出来。这样一来,不同报告期的数据混杂在一起,再按照 rank 排序时,就会出现同一个排名(如第1名)出现多次,或者看起来完全乱序的情况。

正确的解决逻辑:锁定单一报告期

为了避免乱序,正确的逻辑应该是分两步走

  1. 先确定最新一期的报告期stat_datepub_date)。
  2. 锁定该报告期,再按照持有金额(market_value)或占净值比例(proportion)进行降序排列。

query(), filter(), order_by() 的正确使用姿势

  • query():相当于 SQL 的 SELECT,指定你要查哪张表或哪些字段。
  • filter():相当于 SQL 的 WHERE,用于精准过滤。这是防止乱序的关键,必须在这里把日期(stat_date)卡死,确保只查某一期的数据。
  • order_by():相当于 SQL 的 ORDER BY。在锁定了单期数据后,直接使用 .market_value.desc() 即可实现按持有金额从大到小完美排序。

完整代码示例

以下是提取单只 ETF 最新持股明细并按持有金额正确排序的代码:

from jqdata import *

def get_etf_portfolio_sorted(etf_code):
    # 第一步:获取该 ETF 最新的一期统计日期 (stat_date)
    q_date = query(
        finance.FUND_PORTFOLIO_STOCK.stat_date
    ).filter(
        finance.FUND_PORTFOLIO_STOCK.code == etf_code
    ).order_by(
        finance.FUND_PORTFOLIO_STOCK.stat_date.desc()
    ).limit(1)
    
    df_date = finance.run_query(q_date)
    if df_date.empty:
        print(f"未找到 {etf_code} 的持股数据")
        return None
        
    latest_stat_date = df_date['stat_date'][0]
    
    # 第二步:锁定该统计日期,查询持股明细,并按持有市值(market_value)降序排列
    q_portfolio = query(
        finance.FUND_PORTFOLIO_STOCK
    ).filter(
        finance.FUND_PORTFOLIO_STOCK.code == etf_code,
        finance.FUND_PORTFOLIO_STOCK.stat_date == latest_stat_date  # 核心:锁定单期
    ).order_by(
        finance.FUND_PORTFOLIO_STOCK.market_value.desc()            # 按持有金额降序
    ).limit(100)
    
    df_portfolio = finance.run_query(q_portfolio)
    return df_portfolio

# 测试提取华泰柏瑞沪深300ETF (510300.XSHG) 的持股明细
df = get_etf_portfolio_sorted('510300.XSHG')
if df is not None:
    # 打印查看结果,确保没有乱序
    print(df[['code', 'symbol', 'name', 'stat_date', 'market_value', 'proportion']].head(20))

总结

  • 不要在 order_by 中同时处理日期和排名,这会导致跨期数据污染。
  • 永远在 filter 中把日期(stat_date)固定下来,然后再用 order_by 对金额(market_value)进行排序,这样无论加不加 limit,数据都是绝对干净且排序正确的。