← 返回博客列表

【零依赖量化数据实战 #21】北交所公司财务三表:4 个 URL 看资产、盈利与现金流

2026年08月28日 11:08 · 智兔数服 · 零依赖量化数据实战

摘要:【零依赖量化数据实战 #21】北交所公司财务三表:4 个 URL 看资产、盈利与现金流 系列:《零依赖量化数据实战》|零依赖 · 纯 GET · 不 import 任何 SDK 适用:想做北交所(BJ)公司基本面

系列:《零依赖量化数据实战》|零依赖 · 纯 GET · 不 import 任何 SDK
适用:想做北交所(BJ)公司基本面财务分析、把 #18 的「北交所实时行情」升级到「财务面」的开发者

你将得到什么

  • 4 个「北交所财务三表」端点的路径、参数和语义(资产负债表 / 利润表 / 现金流量表 / 财务比率),照公开文档核对过的,不是猜的
  • 一段 fin_sheet(code) 把一家北交所公司的四张表一次性拉齐,整理成「三表 + 比率」概览
  • 字段名容错写法(候选键命中 _hit_key),对字段名不敏感、对返回形态(list / dict)不敏感
  • 完整可复制运行代码,把 你的智兔token 换成真实智兔证书即可直接跑

一、四个端点,一张语义表

北交所公司的财务三表 + 比率,统一走 /bj/fin/ 前缀,股票代码是路径参数(如 920547.BJ),开始/结束时间是可选的查询参数 st / et(格式 YYYYMMDD,不填则为全部历史):

GET https://api.zhituapi.com/bj/fin/balance/{code}?token=<你的智兔token>&st=&et=   资产负债表
GET https://api.zhituapi.com/bj/fin/income/{code}?token=<你的智兔token>&st=&et=   利润表
GET https://api.zhituapi.com/bj/fin/cashflow/{code}?token=<你的智兔token>&st=&et=  现金流量表
GET https://api.zhituapi.com/bj/fin/ratios/{code}?token=<你的智兔token>&st=&et=    财务比率
  • 这是按股票代码拉历史财务的接口,每个端点返回的是多期数据(不同报告期一行)。
  • 与 #17 的 /hs/gs/*(沪深公司简介/财务指标/股东)定位不同:这里是北交所的完整三表 + 比率,颗粒度更细(资产、负债、收入、利润、经营/投资/筹资现金流、ROE、负债率等)。

二、先把"拿数据"这件小事解决

只依赖 requests,不引入任何 SDK。小工具沿用本系列一致写法:

import requests

BASE = "https://api.zhituapi.com"
TOKEN = "你的智兔token"
HEADERS = {"User-Agent": "Mozilla/5.0"}


def fetch(url):
    """GET 一个 JSON 端点,返回 (data, err)。"""
    try:
        r = requests.get(url, headers=HEADERS, timeout=10)
        try:
            return r.json(), None
        except Exception:
            return None, "JSON 解析失败, HTTP %d, body=%s" % (r.status_code, r.text[:200])
    except Exception as e:
        return None, "请求异常: %s" % e


def _hit_key(row, *keys):
    """在 dict 里按候选键顺序命中第一个存在且不空的值。"""
    if not isinstance(row, dict):
        return None
    for k in keys:
        if k in row and row[k] not in (None, ""):
            return row[k]
    return None


def _to_float(s):
    """把 '3.5%' / '1,234.56' / 'NaN' 规整成 float;无法解析返回 None。"""
    if s is None:
        return None
    if isinstance(s, (int, float)):
        return float(s)
    t = str(s).replace("%", "").replace(",", "").strip()
    try:
        v = float(t)
        return v if v == v else None  # NaN -> None
    except Exception:
        return None


def _coerce_list(payload):
    """接口返回可能是 list,也可能是 {'data': [...]};统一成 list。"""
    if isinstance(payload, list):
        return payload
    if isinstance(payload, dict):
        for k in ("data", "list", "items", "result", "rows"):
            if isinstance(payload.get(k), list):
                return payload[k]
    return []

要点:fetch 永远返回 (data, err)_hit_key 用候选键顺序命中,字段名差异不崩;_coerce_list 把 list / {"data":[...]} 两种形态抹平。

三、核心模板:fin_sheet()

把一家北交所公司的四张表一次性拉齐,各取最新一期做概览:

SHEETS = {
    "资产负债表": "/bj/fin/balance",
    "利润表": "/bj/fin/income",
    "现金流量表": "/bj/fin/cashflow",
    "财务比率": "/bj/fin/ratios",
}


def fin_sheet(code, token=TOKEN, st="", et=""):
    """拉取某北交所股票的四张财务表,整理成概览。"""
    out = {}
    for label, path in SHEETS.items():
        url = "%s%s/%s?token=%s" % (BASE, path, code, token)
        if st:
            url += "&st=%s" % st
        if et:
            url += "&et=%s" % et
        data, err = fetch(url)
        if err:
            out[label] = {"err": err, "rows": 0, "latest": None}
            continue
        rows = _coerce_list(data)
        latest = rows[0] if rows else None
        out[label] = {"err": None, "rows": len(rows), "latest": latest}
    return out


if __name__ == "__main__":
    import json
    fs = fin_sheet("920547.BJ")
    print(json.dumps(fs, ensure_ascii=False, indent=2, default=str))

跑起来后,fs["资产负债表"]["rows"] 是报告期期数,fs["财务比率"]["latest"] 是最近一期的 ROE / 负债率等。st / et 不填默认全部历史;想只看某段区间,传 st="20230101"et="20231231" 即可。

四、代码自验结果

离线 selftest(合成数据,仅验证逻辑,非真实行情):

selftest PASS

自验覆盖了:候选键命中(_hit_keydate/rq 等不同字段名下都能取到值)、_to_float12.5% / 1,200.5 / NaN / None 的解析、list 与 {"data":[...]} 两种返回形态的统一、以及 fin_sheet 在合成数据下的整理逻辑(四表各 1 期、总资产 1200.5、ROE 12.5 均被正确解析)。

联网实测(占位 token,真实 HTTP 响应原样保留):

{
  "资产负债表": {"err": "JSON 解析失败, HTTP 404, body=102:Licence证书(你的智兔token)不存在\n", "rows": 0, "latest": null},
  "利润表": {"err": "JSON 解析失败, HTTP 404, body=102:Licence证书(你的智兔token)不存在\n", "rows": 0, "latest": null},
  "现金流量表": {"err": "JSON 解析失败, HTTP 404, body=102:Licence证书(你的智兔token)不存在\n", "rows": 0, "latest": null},
  "财务比率": {"err": "JSON 解析失败, HTTP 404, body=102:Licence证书(你的智兔token)不存在\n", "rows": 0, "latest": null}
}

四个端点都返回 404 102:Licence证书(你的智兔token)不存在 —— 这是鉴权先于路由的结果:路径本身合法,但占位 token 没有对应证书。换上你自己的真实 token,上面的脚本就能直接打印北交所公司财务三表数据。文中所有数字均为占位 token 下的自验结果,不编造任何真实财务数值

五、坑与注意事项

  1. 代码是路径参数,不是查询参数920547.BJ 要拼进路径(/bj/fin/balance/920547.BJ),少了会 404;st/et 才是查询参数。
  2. 市场后缀别省:北交所代码带 .BJ,和沪深 .SH/.SZ 不同,喂错市场后缀拉不到数据。
  3. 返回是多期数组:每个端点返回若干报告期(一行一期),取 rows[0] 是"最新一期"还是"最早一期"以实际返回顺序为准,别假设;必要时按日期字段排序。
  4. 字段名以实际返回为准:文档给的是表语义,具体字段名(总资产是 total_assets 还是别的)以你换真实 token 后的返回为准;本文用 _hit_key 做了容错。
  5. 不要自创接口:本文四个端点全部来自官方文档,未做任何路径拼接或猜测。

六、小结与下篇预告

至此,#09–#21 把数据面完整铺开:基金链(#09–#13) → A股异动/可转债/港股通/公司面(#14–#17) → 北交所实时(#18) → 沪深指数(#19) → 行业板块分类(#20) → 北交所财务三表(#21)。本阶段(#09–#21)收尾。

后续可继续向深度维度扩展:港股通成交排名与历史(/ht/nbzj/*pm/ls 系列)、沪深个股财务三表(/hs/fin/*)、指数技术指标(/hz/history/* 的 MACD/MA/BOLL/KDJ),把数据面从「分类 / 实时」推进到「财务 / 技术」深度分析。

免费领取证书

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

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

把代码里的 你的智兔token 换成你拿到的真实 token,上面的脚本就能直接打印北交所公司财务三表数据。

免责声明

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

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