3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在您的代码中,使用了 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名)出现多次,或者看起来完全乱序的情况。
为了避免乱序,正确的逻辑应该是分两步走:
stat_date 或 pub_date)。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,数据都是绝对干净且排序正确的。