← 返回博客列表

【零依赖量化数据实战 #08】场内基金 ETF 数据接口:清单 / 净值 / 持仓 / 排名一键取

2026年08月22日 09:54 · 智兔数服 · 零依赖量化数据实战

摘要:【零依赖量化数据实战 #08】场内基金 ETF 数据接口:清单 / 净值 / 持仓 / 排名一键取 系列:《零依赖量化数据实战》|零依赖 · 纯 GET · 不 import 任何 SDK 适用:想做基金筛选、E

系列:《零依赖量化数据实战》|零依赖 · 纯 GET · 不 import 任何 SDK 适用:想做基金筛选、ETF 盯盘、持仓归因、业绩排名,不想装库或对接多个数据源的开发者

你将得到什么

  • 基金 / ETF 数据端点的路径家族:清单 / 行情 / 净值 / 概况 / 持仓 / 排名照公开文档核对过的,不是猜的(均列于智兔官网《基金行情 API 文档》)
  • 一段 fund_dashboard() 单只基金穿透 + pull_lists() / pull_ranking() 全市场扫描的完整代码
  • 字段名容错写法:不同数据源字段名不一样,用候选键列表命中抽取,换数据源不用改代码
  • 基金盯盘的最小可用模板:把清单 / 净值 / 持仓 / 排名包成函数,每天跑一次打印
  • 完整可复制运行的代码,把 你的智兔token 换成真实证书即可直接跑

一、端点家族,一张语义表

智兔把基金 / ETF 数据拆成了几个独立端点组,均见于官网公开文档、对客开放:

GET https://api.zhituapi.com/fund/list/all              全量基金清单
GET https://api.zhituapi.com/fund/list/etf              ETF 基金清单
GET https://api.zhituapi.com/jh/hq/list                 基金行情列表(实时报价)
GET https://api.zhituapi.com/jh/base/jjgk/{code}        基金概况(规模/成立日/类型)
GET https://api.zhituapi.com/jh/hb/lsjz/{code}          历史净值(按日)
GET https://api.zhituapi.com/jh/zh/gpcc/{code}          股票持仓(前十大重仓股)
GET https://api.zhituapi.com/jh/zh/zccc/{code}          债券持仓
GET https://api.zhituapi.com/js/pm/kfjzg/kfsjj_gpxjj_zsx   开放式股票型基金净值排名

鉴权统一 ?token=<你的智兔token>fund/list/*jh/hq/list全市场清单,不带 {code}jjgk/lsjz/gpcc/zccc单只基金维度,路径里 {code} 换成基金代码;js/pm/... 排名是固定 key 路径(不同分类对应不同 key)。

一个实际用法:先 fund/list/etf 拿全市场 ETF 清单 → 用 jh/hq/list 给它们排涨跌幅 → 挑出关注的基金用 fund_dashboard(code) 拉概况 + 历史净值 + 十大重仓,做持仓归因。全程纯 GET,不用落地任何库。

二、字段名不固定?用候选键命中

基金端点的返回字段名可能因数据源更新而变化。与其硬编码字段名(改了就全崩),不如用候选键列表命中抽取:

def _hit_key(d, candidates):
    """从字典中按候选键名列表命中第一个存在的键。"""
    for k in candidates:
        if k in d:
            return k
    return None

def summarize(name, data):
    """从基金端点的返回中抽取关键字段做汇总,对字段名不敏感、对形态不敏感。"""
    if data is None:
        return f"  {name}: 无数据"
    if isinstance(data, list):
        if not data:
            return f"  {name}: 0 条"
        first = data[0] if isinstance(data[0], dict) else {}
        code_key = _hit_key(first, ["code", "dm", "fundcode", "jjdm", "symbol"])
        name_key = _hit_key(first, ["name", "mc", "jjmc", "shortname"])
        nav_key = _hit_key(first, ["nav", "dwjz", "jz", "value", "price"])
        parts = [f"  {name}: {len(data)} 条"]
        if code_key:
            parts.append(f"首条代码={first.get(code_key)}")
        if name_key:
            parts.append(f"名称={first.get(name_key)}")
        if nav_key:
            parts.append(f"净值/价键={first.get(nav_key)}")
        return " | ".join(parts)
    if isinstance(data, dict):
        keys = list(data.keys())[:6]
        return f"  {name}: 聚合对象,键={keys}"
    return f"  {name}: {type(data).__name__}"

这样就算数据源把 dm 改成 jjdmfundcode,代码不用动。命不中也不报错——只是那行汇总少一个字段,不影响其他端点。

三、批量拉取 + 基金盯盘模板

import time
import requests

TOKEN = "你的智兔token"
BASE = "https://api.zhituapi.com"

FUND_ENDPOINTS = {
    "list_all": ("/fund/list/all", "全量基金清单"),
    "list_etf": ("/fund/list/etf", "ETF 基金清单"),
    "hq_list":  ("/jh/hq/list", "基金行情列表"),
    "jjgk":     ("/jh/base/jjgk/{code}", "基金概况"),
    "lsjz":     ("/jh/hb/lsjz/{code}", "历史净值"),
    "gpcc":     ("/jh/zh/gpcc/{code}", "股票持仓"),
    "zccc":     ("/jh/zh/zccc/{code}", "债券持仓"),
    "pm":       ("/js/pm/kfjzg/kfsjj_gpxjj_zsx", "开放式股票型基金净值排名"),
}


def fetch_fund(path, token=TOKEN, timeout=15):
    """拉单个基金端点,返回 (data, err)。"""
    try:
        r = requests.get(f"{BASE}{path}", params={"token": token}, timeout=timeout)
    except requests.RequestException as e:
        return None, f"网络异常:{e}"
    if r.status_code != 200:
        return None, f"{r.status_code} {r.text.strip()[:140]}"
    try:
        payload = r.json()
    except ValueError:
        return None, f"非 JSON 返回:{r.text[:140]}"
    if isinstance(payload, dict):
        detail = payload.get("detail") or payload.get("error")
        return None, f"业务错误:{detail or list(payload)[:6]}"
    return payload, None


def pull_lists(token=TOKEN, sleep=0.2):
    """拉取全市场清单类端点(不带 code)。"""
    if token == "你的智兔token":
        print("[演示] TOKEN 为占位符,请换成真实 token 后再跑。")
    for key in ("list_all", "list_etf", "hq_list"):
        path, desc = FUND_ENDPOINTS[key]
        data, err = fetch_fund(path, token)
        if err:
            print(f"  {key} ({desc}): 暂不可用:{err}")
        else:
            print(summarize(key, data))
        time.sleep(sleep)


def fund_dashboard(code, token=TOKEN, sleep=0.2):
    """单只基金穿透:概况 + 历史净值 + 股票持仓 + 债券持仓。"""
    print(f"=== {code} 基金穿透 ===")
    for key in ("jjgk", "lsjz", "gpcc", "zccc"):
        path, desc = FUND_ENDPOINTS[key]
        data, err = fetch_fund(path.format(code=code), token)
        if err:
            print(f"  {key} ({desc}): 暂不可用:{err}")
        else:
            print(summarize(key, data))
        time.sleep(sleep)


def pull_ranking(token=TOKEN):
    """拉取基金业绩排名(固定 key 路径)。"""
    path, desc = FUND_ENDPOINTS["pm"]
    data, err = fetch_fund(path, token)
    if err:
        print(f"  pm ({desc}): 暂不可用:{err}")
    else:
        print(summarize("pm", data))


if __name__ == "__main__":
    pull_lists()
    fund_dashboard("510300")
    pull_ranking()

_hit_key / summarize 见前文,拼在一起就是完整脚本。)

sleep=0.2 是限频保护;清单类 3 个 + 单只 4 个 + 排名 1 个,合计 8 次请求加间隔远在限频内。

四、代码自验结果

离线自测 6 项全 PASS:

selftest PASS: 端点注册完整(8/8) / _hit_key 命中逻辑 / summarize 列表形态抽取
/ summarize 聚合对象形态 / summarize 空数据兜底 / 错误码解析结构 共 6 项

联网跑(占位 token)真实输出:

[演示] TOKEN 为占位符,请换成真实 token 后再跑。
  list_all (全量基金清单): 暂不可用:403 102:Licence证书(你的智兔token)不存在
  list_etf (ETF 基金清单): 暂不可用:403 102:Licence证书(你的智兔token)不存在
  ...

8 个端点契约一致。本文未编造任何基金净值/持仓数值——换成覆盖该接口的正式证书后重跑,即可打印真实基金数据。

五、坑与注意事项

坑 #1:102 不代表路径写对了。 鉴权发生在路由匹配之前。把路径故意写错(/fund/list/nosuch)配无效 token,同样返回 102。路径合法性只能靠客户端按白名单自查——本文 8 个路径是照公开文档核对的。

坑 #2:基金代码形态分两类。 净值 / 持仓类(/jh/hb/lsjz/jh/base/jjgk/jh/zh/*)用裸代码(公开文档示例 000001);行情类(/jh/hq/*)的代码常带 sz/sh 前缀(示例 sz180801)。混用会拿不到数据,按公开文档示例为准。

坑 #3:js/pm/... 排名是固定 key 不是自由参数。 不同分类(股票型 / 货币型 / 封闭型)对应不同 key 串(如 kfsjj_gpxjj_zsx),路径是写死的,别自己拼分类名。

小结与下篇预告

这篇你拿到了基金 / ETF 数据的完整端点家族、字段名容错抽取写法、以及一个「清单扫描 + 单只穿透 + 排名」的基金盯盘模板。

下一批进入基金主题深挖:#09 讲基金持仓穿透/jh/zh/gpcc + /jh/zh/zccc + /jh/base/jjgk 做一只基金的股票/债券持仓归因)。

免费领取证书

数据来自 智兔数服(www.zhituapi.com):零 SDK、纯 GET、免费版即可起步。

领取路径:进入 www.zhituapi.com → 点击「请求证书」→「证书获取」→「免费版」(邮箱验证 3 步即可拿到 token)。

把代码里的 你的智兔token 换成你拿到的真实 token,上面的脚本就能直接打印基金数据。

免责声明

本文仅演示公开数据接口的用法,所有代码示例均以占位 token 自验,未含任何真实基金数据;文中数据仅为接口用法演示,不构成投资建议,亦不承诺收益。投资决策请基于你自己的判断与风险承受力。

想亲自试一下?免费获取证书