A股量化数据工程:从 REST 接口到策略信号(第 4 篇):行业与概念板块树构建
前言
做板块轮动、题材挖掘,第一件事是有一张「板块树」:指数、行业、概念之间谁挂在谁下面,代码怎么对应。接口 hszg/list 直接给出了完整的树形结构(带 type1/type2/level/pcode),本篇把它落库并还原成可遍历的层级,下一篇就能顺着树把成分股挂上去。
一、接口原始字段梳理
1)指数、行业、概念树
接口地址:https://api.biyingapi.com/hszg/list/{LICENCE}
| 字段 | 说明 |
|---|---|
| name | 节点名称(指数/行业/概念名) |
| code | 节点代码 |
| type1 | 一级类型(如 指数 / 行业 / 概念) |
| type2 | 二级类型(细分) |
| level | 层级(0 根、1 一级、2 二级...) |
| pcode | 父节点代码 |
| pname | 父节点名称 |
| isleaf | 是否叶子节点(1 是 / 0 否) |
2)根据指数、行业、概念找相关股票
接口地址:https://api.biyingapi.com/hszg/gg/{指数、行业、概念代码}/{LICENCE}
| 字段 | 说明 |
|---|---|
| dm | 股票代码(带市场后缀) |
| mc | 股票名称 |
| jys | 交易所 |
3)根据股票找相关指数、行业、概念
接口地址:https://api.biyingapi.com/hszg/zg/{股票代码}/{LICENCE}
| 字段 | 说明 |
|---|---|
| code | 关联节点代码 |
| name | 关联节点名称 |
二、自研衍生统计指标
- 层级深度 :由
level还原每个节点到根的路径,便于「概念→行业→指数」上钻。 - 叶子计数 :
isleaf=1的节点即最细颗粒(通常是概念或个股集合),统计每个行业下挂几个概念。 - 反向映射 :用
hszg/zg把「股票→板块」反向建索引,和正向树互为补充。
树结构与反向映射均基于原始
code/pcode业务字段二次计算,接口不返回现成层级对象。
三、数据表设计
sql
CREATE TABLE IF NOT EXISTS board_tree (
code TEXT PRIMARY KEY,
name TEXT,
type1 TEXT,
type2 TEXT,
level INTEGER,
pcode TEXT,
pname TEXT,
isleaf INTEGER
);
CREATE TABLE IF NOT EXISTS board_stock_map (
board_code TEXT,
dm TEXT,
mc TEXT,
jys TEXT,
PRIMARY KEY (board_code, dm)
);
CREATE TABLE IF NOT EXISTS stock_board_map (
dm TEXT,
code TEXT,
name TEXT,
PRIMARY KEY (dm, code)
);
四、完整代码
python
import requests
import sqlite3
import time
LICENCE = "YOUR_LICENCE"
BASE = "https://api.biyingapi.com"
DB_PATH = "quant.db"
def biying_api_get_retry(url, timeout=15, max_retry=3):
for i in range(max_retry):
try:
r = requests.get(url, timeout=timeout)
r.raise_for_status()
return r.json()
except Exception as e:
if i == max_retry - 1:
raise
time.sleep(1 + i)
def fetch_board_tree():
data = biying_api_get_retry(f"{BASE}/hszg/list/{LICENCE}")
return data.get("data", []) if isinstance(data, dict) else []
def fetch_stocks_of_board(bcode):
data = biying_api_get_retry(f"{BASE}/hszg/gg/{bcode}/{LICENCE}")
return data.get("data", []) if isinstance(data, dict) else []
def fetch_boards_of_stock(dm):
data = biying_api_get_retry(f"{BASE}/hszg/zg/{dm}/{LICENCE}")
return data.get("data", []) if isinstance(data, dict) else []
def save_tree(rows):
con = sqlite3.connect(DB_PATH)
con.executemany(
"INSERT OR REPLACE INTO board_tree"
"(code,name,type1,type2,level,pcode,pname,isleaf)"
"VALUES(?,?,?,?,?,?,?,?)",
[[r.get("code"), r.get("name"), r.get("type1"), r.get("type2"),
r.get("level"), r.get("pcode"), r.get("pname"), r.get("isleaf")]
for r in rows])
con.commit(); con.close()
def build_path(rows):
"""自研:由 pcode 还原每个节点的祖先链。"""
by_code = {r.get("code"): r for r in rows}
def ancestors(code, acc=None):
acc = acc or []
node = by_code.get(code)
if not node or not node.get("pcode"):
return acc
p = by_code.get(node["pcode"])
if p:
acc.insert(0, p.get("name"))
return ancestors(p["code"], acc)
return acc
return {r.get("code"): ancestors(r.get("code")) for r in rows}
if __name__ == "__main__":
tree = fetch_board_tree()
save_tree(tree)
paths = build_path(tree)
leaves = [r for r in tree if r.get("isleaf") == 1]
print(f"板块节点共 {len(tree)} 个,叶子(最细) {len(leaves)} 个")
# 演示:取一个概念节点,看它的祖先链与成分股
sample = leaves[0] if leaves else {}
bcode = sample.get("code")
if bcode:
print(f"示例节点 {sample.get('name')} 祖先链:", " > ".join(paths.get(bcode, [])))
stocks = fetch_stocks_of_board(bcode)
print(f" 成分股示例:", stocks[:3])
五、关键业务说明
pcode是树的根 :所有层级关系靠pcode外键自指,落库后用build_path自研还原路径,无需接口给对象。- 叶子即最细颗粒 :
isleaf=1通常对应概念或个股集合,是做成分股映射的最小单元。 - 正反向两张表 :
board_stock_map正向(板块→股)、stock_board_map反向(股→板块),查询时双向命中。
六、拓展练习
- 统计每个一级行业(
type1=行业)下挂多少个概念叶子,做一个「行业---概念」宽度榜。 - 用
stock_board_map找出同时属于「新能源车」与「锂电池」两个概念的股票。 - 把
build_path输出成 JSON 树,供前端板块选择器直接渲染。
下篇预告
A股量化数据工程:从 REST 接口到策略信号(第 5 篇):板块成分股映射与对齐
下一篇把
hszg/gg的成分股批量落进board_stock_map,并演示如何把板块涨跌与成分股行情做对齐,得到「板块强弱 = 成分股中位数涨跌幅」。
免责申明 :本篇全部代码、接口调用仅作为量化编程学习演示,仅为数据演示,不构成投资建议。市场有风险,投资需谨慎。