【零依赖量化数据实战 #30】沪深板块与股票列表:新股·板块·主板·成分股

0 阅读4分钟

【零依赖量化数据实战 #30】沪深板块与股票列表:新股·板块·主板·成分股

系列:《零依赖量化数据实战》|零依赖 · 纯 GET · 不 import 任何 SDK 适用:想用 Python 把沪深的板块分类、新股/主板/创业板列表、以及某板块成分股一次拉齐的量化爱好者;不依赖任何行情终端。

1. 你将得到什么

  • 4 个官方接口的最小可用封装,分两组:
    • 列表(3,均无参):/hs/list/new 新股列表、/hs/list/sectors 板块列表、/hs/list/primary 主板/一级行业列表。
    • 板块成分(1,带路径参数):/hs/sectors/{板块指数名称} 取某板块的成分股(如 概念指数行业指数)。
  • 一个对字段名不敏感的排名函数 rank_by:按候选键(如 涨停/zt/limit_up)降序取前 N。

2. 端点语义表

GET https://api.zhituapi.com/hs/list/new?token=你的token        -> 新股列表
GET https://api.zhituapi.com/hs/list/sectors?token=你的token    -> 板块列表
GET https://api.zhituapi.com/hs/list/primary?token=你的token    -> 主板/一级行业列表
GET https://api.zhituapi.com/hs/sectors/概念指数?token=你的token -> 某板块成分股(板块名路径参数)

鉴权:token 走查询参数;/hs/sectors/{板块指数名称} 的板块名是路径参数(中文需 URL 编码),不是查询参数。

3. 字段名不固定?用候选键命中

板块/列表返回的「涨停」可能叫 涨停 / zt / limit_up。统一候选键命中:

def _hit_key(d, keys):
    if not isinstance(d, dict):
        return None
    for k in keys:
        if k in d and d[k] is not None:
            return d[k]
    low = {str(x).lower(): x for x in d.keys()}
    for k in keys:
        kl = k.lower()
        if kl in low:
            return d[low[kl]]
    return None

4. 核心模板函数

import sys, requests

BASE = "https://api.zhituapi.com"
TOKEN = "你的token"  # 占位,换成你申请的真实 token

def _hit_key(d, keys):
    if not isinstance(d, dict):
        return None
    for k in keys:
        if k in d and d[k] is not None:
            return d[k]
    low = {str(x).lower(): x for x in d.keys()}
    for k in keys:
        kl = k.lower()
        if kl in low:
            return d[low[kl]]
    return None

def _to_float(v):
    try:
        return None if v is None else float(v)
    except (TypeError, ValueError):
        return None

def _get(path, params=None):
    p = dict(params or {})
    p["token"] = TOKEN
    try:
        r = requests.get(f"{BASE}{path}", params=p, timeout=10)
    except Exception as e:
        return None, f"网络异常:{e}"
    if r.status_code != 200:
        return None, f"{r.status_code} {r.text.strip()[:140]}"
    try:
        return r.json(), None
    except Exception:
        return None, f"非 JSON:{r.text.strip()[:140]}"

# 沪深股票/板块列表(3 个,无参)
def fetch_list(kind):
    return _get(f"/hs/list/{kind}")

# 板块成分(带 {板块指数名称} 路径参数,如 概念指数)
def fetch_sector(name):
    return _get(f"/hs/sectors/{name}")

def rank_by(rows, keys, descending=True, topn=None):
    if not isinstance(rows, list):
        return rows
    def sc(x):
        return _to_float(_hit_key(x, keys)) or 0.0
    out = sorted(rows, key=sc, reverse=descending)
    return out[:topn] if topn else out

def run_check():
    # 合成数据仅逻辑校验,非真实行情
    rows = [
        {"code": "A", "名称": "平安", "涨停": 1},
        {"code": "B", "name": "茅台", "zt": 0},
        {"code": "C", "股票名": "宁德", "limit_up": 1},
    ]
    top = rank_by(rows, ["涨停", "zt", "limit_up"], topn=2)
    assert [x["code"] for x in top] == ["A", "C"], top
    for kind in ("new", "sectors", "primary"):
        assert kind in ("new", "sectors", "primary")
    print("校验通过")

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--run_check":
        run_check()
    else:
        for kind in ("new", "sectors", "primary"):
            print(f"list.{kind} ->", fetch_list(kind))
        print("sectors.概念指数 ->", fetch_sector("概念指数"))

跑通示例

把上面的代码复制到本地,填入你的 token 即可直接运行:它会请求对应接口、拉取真实数据,并输出归一化后的结构化字典(各字段含义见前文各小节)。

6. 坑与注意事项

  1. 102 不代表路径对404 102 是「证书不存在」(鉴权先于路由),路径合法与否要靠客户端白名单自查。
  2. 板块名是路径参数/hs/sectors/{板块指数名称} 的板块名拼在路径里(如 概念指数),中文要 URL 编码,不要写成查询参数。
  3. 两个「板块」接口别混/hs/list/sectors 返回「板块清单」,/hs/sectors/{name} 返回「某板块的成分股」,用途不同。
  4. 字段名中英文混用:「涨停」可能叫 涨停/zt/limit_up,务必候选键命中。

7. 小结与下篇预告

本篇把「沪深板块分类 + 新股/主板列表 + 板块成分」拧成了 4 个零依赖接口的最小封装,重点解决了板块名路径参数与 URL 编码两个板块接口区分两个坑,配 rank_by 候选键排名即可一行出榜。

下一篇计划写 #31《沪深实时盘口全景:逐笔·全盘实时·最新价·历史逐笔》:讲解如何用官方接口拉取沪深逐笔交易(/hs/real/zbjy)、全盘实时(/hs/public/realall/hs/custom/realall)、最新价与历史逐笔(/hs/latest/hs/history/transaction)等实时盘口数据。

8. 免责声明

本文仅演示公开数据接口的用法,所有代码示例均为演示数据,未含任何真实数据;文中示例仅为演示用途,不构成投资建议,亦不承诺收益。