Infoway API 返回的财务数据,每一条记录都由五个核心字段描述,包括:
itemIditemNameitemGroupitemValueparentItemId
初看会觉得有点抽象,但理解之后你会发现它的设计非常规整,无论是损益表、现金流量表还是资产负债表,用的都是同一套字段体系。这篇内容将帮助你更好地理解我们的财报API,尤其是对于没有财务会计背景的同学。
1. itemId: 财务科目的机器编号
itemId 是每个财务科目的固定英文标识符,比如 net_income(净利润)、gross_profit(毛利润)、total_revenue(总收入)。
它的作用类似数据库里的主键:不管你查的是 A 股还是美股,不管返回的是中文名还是英文名,itemId 永远固定不变。这意味着你在代码里过滤数据时,应该始终用 itemId 做判断,而不是用 itemName,比如:
# 正确做法:用 itemId 过滤
net_income_items = [i for i in data["data"] if i["itemId"] == "net_income"]
# 不推荐:用 itemName 可能因语言差异出错
net_income_items = [i for i in data["data"] if i["itemName"] == "净利润"]2. itemName:科目的可读名称
itemName 是 itemId 对应的英文显示名称,比如 gross_profit 对应的 itemName 是 "Gross Profit",net_income 对应的是 "Net Income"。itemName 用于展示,itemId 用于代码逻辑中的过滤和判断。
这个字段只会返回英文,不会出现中文或其他语言。如果你的产品需要向用户展示本地化名称,需要自己维护一份 itemId 到目标语言的映射表:
# 自定义中文名称映射
ITEM_NAMES_ZH = {
"total_revenue": "总收入",
"cost_of_goods": "营业成本",
"gross_profit": "毛利润",
"operating_expenses": "运营费用",
"operating_income": "营业利润",
"net_income": "净利润",
"cf_oper": "经营活动现金流",
"cf_invest": "投资活动现金流",
"cf_finance": "筹资活动现金流",
"total_assets": "总资产",
"total_liabilities": "总负债",
"total_equity": "股东权益合计",
}
# 使用时:优先取自定义名称,没有则回退到 API 返回的英文
def get_display_name(item, lang="zh"):
if lang == "zh":
return ITEM_NAMES_ZH.get(item["itemId"], item["itemName"])
return item["itemName"]3. itemGroup:数据所属的报表分类
itemGroup 是一个固定的英文字符串,标识这条数据来自哪张财报或哪个指标类别。
这个字段在需要过滤特定类型数据时很有用。常见的 itemGroup 值包括:
| itemGroup | 含义 |
| Income Statement | 损益表科目 |
| Cash Flow Statement | 现金流量表科目 |
| Balance Sheet | 资产负债表科目 |
| Valuation ratios | 估值比率(PE、PB 等) |
| Profitability ratios | 盈利能力指标(ROE、毛利率等) |
| Revenue by business | 按业务板块拆分的收入 |
| Revenue by region | 按地区拆分的收入 |
| Dividend | 股息相关指标 |
# 只取估值类指标
valuation = [i for i in data["data"] if i["itemGroup"] == "Valuation ratios"]4. itemValue:当期科目的数值
itemValue 是该科目在当前报告期的原始数值,单位是对应市场的货币基本单位,A 股是元(人民币),美股是美元,港股是港元。
你会注意到这些数字通常非常大,比如 21340000000。这是正常的,因为 API 直接返回原始值,没有做单位换算。展示时需要自己处理:
val = item["itemValue"]
print(f"{val / 1e8:.2f} 亿") # → 213.40 亿(适合 A 股展示)
print(f"${val / 1e9:.1f}B") # → $21.3B(适合美股展示)另外,itemValue 可以是负数。投资活动现金流通常为负,表示净资金流出,这是正常的财务含义,并非数据错误。
5. parentItemId:父科目的编号
这是五个字段里最需要花时间理解的一个。
财报中的科目存在上下级关系:毛利润是总收入的子项,运营费用是营业利润的子项。parentItemId 记录的就是当前项目的上级科目的 itemId 是什么。
API 返回的是一个扁平的列表,每条记录平铺在数组里,没有嵌套。但通过 parentItemId,你可以把这个扁平列表还原成树状的层级结构:
total_revenue(总收入) ← parentItemId = ""(顶层,无父级)
├── cost_of_goods(营业成本) ← parentItemId = "total_revenue"
└── gross_profit(毛利润) ← parentItemId = "total_revenue"
operating_income(营业利润) ← parentItemId = ""(顶层)
└── operating_expenses(运营费用)← parentItemId = "operating_income"
net_income(净利润) ← parentItemId = ""(顶层)判断一个科目是否为顶层:parentItemId 为空字符串 "" 时,说明它没有父级,是顶层科目。注意这里是空字符串,不是 None,代码判断时要用 if item["parentItemId"] 而不是 if item["parentItemId"] is not None。
下面是把 API 返回的扁平列表重建为树形结构的完整代码:
# 取最新报告期
latest_date = data["data"][0]["periodDate"]
items = [i for i in data["data"] if i["periodDate"] == latest_date]
# 建立 id → 节点 的映射
nodes = {i["itemId"]: {**i, "children": []} for i in items}
# 根据 parentItemId 挂载父子关系
roots = []
for item in items:
pid = item.get("parentItemId", "")
if pid and pid in nodes:
nodes[pid]["children"].append(nodes[item["itemId"]])
else:
roots.append(nodes[item["itemId"]])
# 递归打印树
def print_tree(node, depth=0):
indent = " " * depth
val = node["itemValue"]
print(f"{indent}{node['itemName']}({node['itemId']}): {val / 1e8:.2f} 亿")
for child in node.get("children", []):
print_tree(child, depth + 1)
for root in roots:
print_tree(root)6. 三个值得注意的细节
1. 同一个 itemId 会出现多次
API 会把多个报告期的数据混在同一个数组里返回。如果不加过滤,net_income 这个科目可能出现十几条,分别对应不同季度。取最新一期数据的正确方式:
latest_date = data["data"][0]["periodDate"] # 第一条即最新期
latest = [i for i in data["data"] if i["periodDate"] == latest_date]2. parentItemId 是空字符串,不是 None
顶层科目的 parentItemId 值是 ""(空字符串)。条件判断写 if item["parentItemId"] 即可——空字符串在 Python 中为假值,这样写能正确区分顶层和子级科目。
3. itemValue 的货币单位因市场而异
A 股的 itemValue 单位是人民币元,美股是美元,港股是港元,日股是日元。跨市场比较时,注意先统一换算货币,不能直接用原始数值做对比。