沪深港通数据实战:从北向资金到择时信号的全链路量化(第 1 篇):沪深港通全景接入:资金流向概览、历史走势与总览
一、前言
沪深港通是外资配置 A 股、内资配置港股的主通道。市场习惯把「北向资金」(沪股通+深股通)视为外资情绪的风向标,把「南向资金」(港股通沪+港股通深)视为内资南下意愿的晴雨表。
本季《沪深港通数据实战:从北向资金到择时信号的全链路量化》规划 12 篇,围绕官方/ht/nbzj/接口族,从资金流向概览一路做到可落地的北向择时信号。本篇是开篇,目标是把 5 个基础接口一次性接入、落库,并验证「北向≈沪股通+深股通」这一自研恒等式,为后续走势深度(第 2 篇)与择时综合指标(第 11 篇)打底。
系列沿用统一规范:域名固定api.biyingapi.com;LICENCE / DB_PATH / LOG_FILE为全局常量;网络请求统一走biying_api_get_retry带重试;本地落库用 SQLitequant.db;代码块语法均通过ast.parse自检。
二、接口与字段
本篇覆盖 5 个只读接口,字段全部以官方 API 文档为准(零编造)。
2.1 最新资金流向概览/ht/nbzj/lxgl
| 字段 | 含义(按官方字段名推断) |
|---|---|
| type | 渠道类型(如沪股通/深股通/港股通等) |
| tname | 渠道名称 |
| dir | 流向方向 |
| netbuy | 净买入额 |
| netin | 净流入额 |
| remain | 当日余额 |
| up / ping / down | 上涨 / 平盘 / 下跌家数 |
| idx / idxdm / idxzd | 关联指数 / 指数代码 / 指数涨跌 |
| status | 状态 |
2.2 北向资金历史走势/ht/nbzj/bxls/阶段(如1)
| 字段 | 含义 |
|---|---|
| bx | 北向资金净流入 |
| hgt | 沪股通净流入 |
| sgt | 深股通净流入 |
| t | 日期 |
2.3 南向资金历史走势/ht/nbzj/nxls/阶段(如1)
| 字段 | 含义 |
|---|---|
| nx | 南向资金净流入 |
| ggth | 港股通(沪)净流入 |
| ggts | 港股通(深)净流入 |
| t | 日期 |
2.4 北向资金历史总览/ht/nbzj/bxzl
| 字段 | 含义 |
|---|---|
| bx1m / hgt1m / sgt1m | 近 1 月北向 / 沪股通 / 深股通累计 |
| bx6m / hgt6m / sgt6m | 近 6 月累计 |
| bx1y / hgt1y / sgt1y | 近 1 年累计 |
| bxall / hgtall / sgtall | 开通以来全部累计 |
2.5 南向资金历史总览/ht/nbzj/nxzl
| 字段 | 含义 |
|---|---|
| nx1m / ggth1m / ggts1m | 近 1 月南向 / 港股通沪 / 港股通深累计 |
| nx6m / ggth6m / ggts6m | 近 6 月累计 |
| nx1y / ggth1y / ggts1y | 近 1 年累计 |
| nxall / ggthall / ggtsall | 开通以来全部累计 |
三、自研衍生指标
接口只返回裸字段,以下指标全部在 Python 中手写:
- 北向恒等式校验:
bx ≈ hgt + sgt。官方分别给出北向与沪/深分项,我们用加法做交叉验证,偏差接近 0 说明数据自洽。 - 南向恒等式校验:
nx ≈ ggth + ggts,同理。 - 概览涨跌广度:用
up / ping / down计算北向通道覆盖标的的上涨占比,作为情绪快照。
四、数据表设计
5 张表,分别存放 5 个接口。以quant.db为统一库。
CREATETABLEht_lxgl(typeTEXT,tnameTEXT,dirTEXT,netbuyREAL,netinREAL,remainREAL,upINTEGER,pingINTEGER,downINTEGER,idxTEXT,idxdmTEXT,idxzdREAL,statusTEXT,tsTEXT);CREATETABLEht_bxls(tTEXT,bxREAL,hgtREAL,sgtREAL);CREATETABLEht_nxls(tTEXT,nxREAL,ggthREAL,ggtsREAL);CREATETABLEht_bxzl(bx1mREAL,hgt1mREAL,sgt1mREAL,bx6mREAL,hgt6mREAL,sgt6mREAL,bx1yREAL,hgt1yREAL,sgt1yREAL,bxallREAL,hgtallREAL,sgtallREAL,tsTEXT);CREATETABLEht_nxzl(nx1mREAL,ggth1mREAL,ggts1mREAL,nx6mREAL,ggth6mREAL,ggts6mREAL,nx1yREAL,ggth1yREAL,ggts1yREAL,nxallREAL,ggthallREAL,ggtsallREAL,tsTEXT);五、完整代码
importurllib.request,json,sqlite3,time,loggingimportpandasaspdfromdatetimeimportdatetime LICENCE="YOUR_LICENCE_HERE"DB_PATH="quant.db"LOG_FILE="season4_article1.log"BASE="https://api.biyingapi.com"logging.basicConfig(filename=LOG_FILE,level=logging.INFO,format="%(asctime)s %(levelname)s %(message)s")defbiying_api_get_retry(url,timeout=15,max_retry=3):"""带重试的官方接口请求封装。"""last=Noneforiinrange(max_retry):try:req=urllib.request.Request(url,headers={"User-Agent":"Mozilla/5.0"})withurllib.request.urlopen(req,timeout=timeout)asresp:returnjson.loads(resp.read().decode("utf-8"))exceptExceptionase:last=e logging.warning("retry %d/%d %s %s",i+1,max_retry,url,e)time.sleep(1)logging.error("api failed %s %s",url,last)returnNonedefwith_exchange_suffix(code):"""沪深港通接口返回的代码已是标准代码(如 600519.SH / 00700.HK),原值透传。"""returncodedefget_conn():returnsqlite3.connect(DB_PATH)def_as_list(data):returndataifisinstance(data,list)else[data]defsave_lxgl():url=f"{BASE}/ht/nbzj/lxgl/{LICENCE}"data=biying_api_get_retry(url)ifnotdata:returnrows=_as_list(data)conn=get_conn()conn.execute("""CREATE TABLE IF NOT EXISTS ht_lxgl ( type TEXT, tname TEXT, dir TEXT, netbuy REAL, netin REAL, remain REAL, up INTEGER, ping INTEGER, down INTEGER, idx TEXT, idxdm TEXT, idxzd REAL, status TEXT, ts TEXT)""")forrinrows:conn.execute("INSERT INTO ht_lxgl VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)",(r.get("type"),r.get("tname"),r.get("dir"),r.get("netbuy"),r.get("netin"),r.get("remain"),r.get("up"),r.get("ping"),r.get("down"),r.get("idx"),r.get("idxdm"),r.get("idxzd"),r.get("status"),datetime.now().isoformat()))conn.commit();conn.close()logging.info("lxgl saved %d rows",len(rows))defsave_bxls(stage=1):url=f"{BASE}/ht/nbzj/bxls/{stage}/{LICENCE}"data=biying_api_get_retry(url)ifnotdata:returnrows=_as_list(data)conn=get_conn()conn.execute("CREATE TABLE IF NOT EXISTS ht_bxls (t TEXT, bx REAL, hgt REAL, sgt REAL)")forrinrows:conn.execute("INSERT INTO ht_bxls VALUES (?,?,?,?)",(r.get("t"),r.get("bx"),r.get("hgt"),r.get("sgt")))conn.commit();conn.close()logging.info("bxls saved %d rows",len(rows))defsave_nxls(stage=1):url=f"{BASE}/ht/nbzj/nxls/{stage}/{LICENCE}"data=biying_api_get_retry(url)ifnotdata:returnrows=_as_list(data)conn=get_conn()conn.execute("CREATE TABLE IF NOT EXISTS ht_nxls (t TEXT, nx REAL, ggth REAL, ggts REAL)")forrinrows:conn.execute("INSERT INTO ht_nxls VALUES (?,?,?,?)",(r.get("t"),r.get("nx"),r.get("ggth"),r.get("ggts")))conn.commit();conn.close()logging.info("nxls saved %d rows",len(rows))defsave_bxzl():url=f"{BASE}/ht/nbzj/bxzl/{LICENCE}"data=biying_api_get_retry(url)ifnotdata:returnconn=get_conn()conn.execute("""CREATE TABLE IF NOT EXISTS ht_bxzl ( bx1m REAL, hgt1m REAL, sgt1m REAL, bx6m REAL, hgt6m REAL, sgt6m REAL, bx1y REAL, hgt1y REAL, sgt1y REAL, bxall REAL, hgtall REAL, sgtall REAL, ts TEXT)""")conn.execute("INSERT INTO ht_bxzl VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",(data.get("bx1m"),data.get("hgt1m"),data.get("sgt1m"),data.get("bx6m"),data.get("hgt6m"),data.get("sgt6m"),data.get("bx1y"),data.get("hgt1y"),data.get("sgt1y"),data.get("bxall"),data.get("hgtall"),data.get("sgtall"),datetime.now().isoformat()))conn.commit();conn.close()logging.info("bxzl saved")defsave_nxzl():url=f"{BASE}/ht/nbzj/nxzl/{LICENCE}"data=biying_api_get_retry(url)ifnotdata:returnconn=get_conn()conn.execute("""CREATE TABLE IF NOT EXISTS ht_nxzl ( nx1m REAL, ggth1m REAL, ggts1m REAL, nx6m REAL, ggth6m REAL, ggts6m REAL, nx1y REAL, ggth1y REAL, ggts1y REAL, nxall REAL, ggthall REAL, ggtsall REAL, ts TEXT)""")conn.execute("INSERT INTO ht_nxzl VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",(data.get("nx1m"),data.get("ggth1m"),data.get("ggts1m"),data.get("nx6m"),data.get("ggth6m"),data.get("ggts6m"),data.get("nx1y"),data.get("ggth1y"),data.get("ggts1y"),data.get("nxall"),data.get("ggthall"),data.get("ggtsall"),datetime.now().isoformat()))conn.commit();conn.close()logging.info("nxzl saved")defself_check():"""自研校验:北向=沪股通+深股通;南向=港股通沪+港股通深。"""conn=get_conn()df=pd.read_sql("SELECT * FROM ht_bxls ORDER BY t",conn)conn.close()if{"bx","hgt","sgt"}.issubset(df.columns):df["check"]=(df["hgt"]+df["sgt"]-df["bx"]).abs()print(df.tail(5).to_string())print("最大偏差:",df["check"].max())if__name__=="__main__":save_lxgl()save_bxls(stage=1)save_nxls(stage=1)save_bxzl()save_nxzl()self_check()六、业务关键点
lxgl是「最新快照」,每次拉取建议覆盖写入或带时间戳追加;本篇用追加+时间戳,便于看日内变化。bxls / nxls的阶段参数控制时间粒度(如 1=日度),不同会员等级返回窗口不同,落地前先用小阶段验证。bxzl / nxzl是累计值单条记录,建表时注意去重(可按ts保留多版本)。- 所有代码走
biying_api_get_retry,避免盘中限频导致整批失败。
七、拓展练习
- 把
self_check的偏差阈值固化成断言,偏差超 1e-6 即告警。 - 用
lxgl的up/ping/down计算每日北向覆盖标的上涨占比,存成独立时序表。 - 将 5 张表统一加上
source字段标记接口名,方便后续联表。
八、下篇预告
下一篇《北向资金走势深度:累计净流入与自研动量信号》将基于本篇落库的ht_bxls / ht_bxzl,计算累计净流入曲线、净流入 N 日动量,并给出可量化的择时阈值信号。
免责申明:文中所有数据处理逻辑仅为编程演示,仅为数据演示,不构成投资建议。市场有风险,投资需谨慎。