A股量化数据工程:从 REST 接口到策略信号(第 5 篇):板块成分股映射与对齐
前言
第 4 篇拿到了板块树,叶子节点下挂的就是成分股。本篇把成分股批量落进 board_stock_map,并用股票实时行情把「板块」和「个股涨跌」对齐,得到一个最朴素的板块强弱指标:成分股涨跌幅中位数。这一步是把树变成可量化信号的关键。
一、接口原始字段梳理
1)板块成分股
接口地址:https://api.biyingapi.com/hszg/gg/{板块代码}/{LICENCE}
| 字段 | 说明 |
|---|---|
| dm | 股票代码(带市场后缀,如 600519.SH) |
| mc | 股票名称 |
| jys | 交易所 |
2)股票实时行情(用于对齐涨跌)
接口地址:https://api.biyingapi.com/hsstock/real/time/{股票代码.市场}/{LICENCE}
| 字段 | 说明 |
|---|---|
| p | 最新价 |
| o | 今开 |
| h | 最高 |
| l | 最低 |
| yc | 昨收 |
| cje | 成交额 |
| v | 成交量 |
| ud | 涨跌额 |
| pc | 涨跌幅 |
| zf | 振幅 |
| t | 行情时间 |
| pe | 市盈率 |
| tr | 换手率 |
| pb_ratio | 市净率 |
| tv | 成交量(原始) |
二、自研衍生统计指标
- 板块强弱(中位数) :板块内成分股
pc的中位数,规避个别大权重股失真。 - 上涨占比 :
pc > 0的成分股比例,衡量板块内部广度。 - 龙头偏离:板块强弱减去其最大权重股涨跌幅,捕捉「权重拖累 / 题材发散」。
以上均基于原始
pc字段二次计算,接口不返回板块聚合值。
三、数据表设计
sql
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_realtime (
dm TEXT,
t TEXT,
p REAL, o REAL, h REAL, l REAL, yc REAL,
cje REAL, v REAL, ud REAL, pc REAL, zf REAL,
pe REAL, tr REAL, pb_ratio REAL, tv REAL,
PRIMARY KEY (dm, t)
);
CREATE TABLE IF NOT EXISTS board_strength (
board_code TEXT,
t TEXT,
median_pc REAL,
up_ratio REAL,
PRIMARY KEY (board_code, t)
);
四、完整代码
python
import requests
import sqlite3
import time
import statistics
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_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_stock_realtime(dm):
if "." not in dm:
return {}
code, market = dm.split(".", 1)
url = f"{BASE}/hsstock/real/time/{code}.{market}/{LICENCE}"
data = biying_api_get_retry(url)
return data.get("data", {}) if isinstance(data, dict) else {}
def save_stock_map(bcode, rows):
con = sqlite3.connect(DB_PATH)
con.executemany(
"INSERT OR REPLACE INTO board_stock_map(board_code,dm,mc,jys) VALUES(?,?,?,?)",
[[bcode, r.get("dm"), r.get("mc"), r.get("jys")] for r in rows])
con.commit(); con.close()
def board_strength(bcode, t):
con = sqlite3.connect(DB_PATH)
rows = con.execute(
"SELECT dm FROM board_stock_map WHERE board_code=?", (bcode,)).fetchall()
con.close()
pcs = []
for (dm,) in rows:
rt = fetch_stock_realtime(dm)
if rt.get("pc") is not None:
pcs.append(rt["pc"])
# 顺便落实时
con = sqlite3.connect(DB_PATH)
con.execute(
"INSERT OR REPLACE INTO stock_realtime"
"(dm,t,p,o,h,l,yc,cje,v,ud,pc,zf,pe,tr,pb_ratio,tv)"
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
[dm, rt.get("t"), rt.get("p"), rt.get("o"), rt.get("h"),
rt.get("l"), rt.get("yc"), rt.get("cje"), rt.get("v"),
rt.get("ud"), rt.get("pc"), rt.get("zf"), rt.get("pe"),
rt.get("tr"), rt.get("pb_ratio"), rt.get("tv")])
con.commit(); con.close()
if not pcs:
return None, None
median_pc = round(statistics.median(pcs), 3)
up_ratio = round(sum(1 for x in pcs if x > 0) / len(pcs), 3)
con = sqlite3.connect(DB_PATH)
con.execute(
"INSERT OR REPLACE INTO board_strength(board_code,t,median_pc,up_ratio) VALUES(?,?,?,?)",
(bcode, t, median_pc, up_ratio))
con.commit(); con.close()
return median_pc, up_ratio
if __name__ == "__main__":
bcode = "BK0735" # 示例:某个概念板块代码,实际以 hszg/list 返回为准
stocks = fetch_stocks_of_board(bcode)
save_stock_map(bcode, stocks)
print(f"板块 {bcode} 成分股 {len(stocks)} 只,示例:", stocks[:3])
mp, ur = board_strength(bcode, "2026-09-20")
print(f"板块强弱(成分股涨跌幅中位数)={mp} 上涨占比={ur}")
五、关键业务说明
- 成分股是股票不是指数 :对齐涨跌要用
hsstock/real/time的pc,而非指数接口。 - 中位数优于均值:成分股里常有 1 只权重股拉偏均值,中位数更能代表板块真实温度。
- 批量拉取限速:板块成分股可能几十到几百只,注意免费版每日调用上限,建议缓存实时结果、定时刷新。
六、拓展练习
- 对
hszg/list里所有概念叶子节点批量算board_strength,做一次「全概念强弱榜」。 - 把「上涨占比」与「中位数涨跌幅」做成散点,圈出「高广度 + 强涨幅」的活跃板块。
- 用
board_strength历史序列(定时落库)计算板块动量,找持续性强的主线。
下篇预告
A股量化数据工程:从 REST 接口到策略信号(第 6 篇):板块轮动资金监控
下一篇用
hizj/hibk/higg拉取行业、概念、个股的资金流向与净流入排名,把「价格强弱」升级成「资金强弱」,捕捉主力进出的板块轮动。
免责申明 :本篇全部代码、接口调用仅作为量化编程学习演示,仅为数据演示,不构成投资建议。市场有风险,投资需谨慎。