搭建宏观事件的市场赔率仪表盘

宏观赔率仪表盘需要主题列表,以及每个卡片一次事件读取。Narwhal 以四种命名形态提供 CPI、非农就业、失业率、衰退和央行决议的市场赔率:scalar、binary、timing 和 extreme。

列出为仪表盘提供内容的主题

仪表盘上的每个卡片对应一个主题。主题端点会列出它们,并接受可选的 group 参数。宏观分组有 central_banks、us_prices、us_jobs、us_activity、global_activity 和 global_prices。这个请求指定 us_jobs,并只保留前两个主题。

curl,美国就业主题
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/predictions/topics?group=us_jobs"
响应,已精简
{
  "topics": [
    {
      "topic_id": "usa-payrolls",
      "group": "us_jobs",
      "title": "US nonfarm payrolls change",
      "shape": "scalar",
      "unit": "jobs",
      "unit_key": "job",
      "venues": [
        "kalshi"
      ],
      "resolution_kind": "release",
      "calendar": {
        "country": "USA",
        "family": "labour"
      },
      "next_event_id": "usa-payrolls-2026-10"
    },
    {
      "topic_id": "usa-unemployment",
      "group": "us_jobs",
      "title": "US unemployment rate",
      "shape": "scalar",
      "unit": "percent",
      "unit_key": "percent",
      "venues": [
        "kalshi",
        "polymarket"
      ],
      "resolution_kind": "release",
      "calendar": {
        "country": "USA",
        "family": "labour"
      },
      "next_event_id": "usa-unemployment-2026-10"
    }
  ]
}

shape 告诉你该调用哪个端点:scalar、binary、timing 或 extreme。unit 给出答案的单位,venues 列出背后的市场,resolution_kind 说明该主题如何结算。next_event_id 是当前应展示的事件。有些主题(例如 deu-gdp-yoy)没有 next_event_id,脚本会跳过它们。

读取一个事件:衰退赔率

binary 事件提出的是是非题。这个请求读取衰退市场,响应中不包含各市场的规则文本。

curl,binary 事件
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/predictions/binary/usa-recession-2026"
响应,已精简
{
  "event_id": "usa-recession-2026",
  "topic_id": "usa-recession-2026",
  "question": "US recession by end of 2026?",
  "status": "trading",
  "closes_at": "2026-12-31T00:00:00Z",
  "resolves_at": "2026-12-31T00:00:00Z",
  "as_of": "2026-11-03T02:21:00Z",
  "stale": false,
  "resolution": "Two consecutive negative quarters of US real GDP from Q2 2025 to Q4 2026, or an NBER recession call for 2025 or 2026",
  "venues": [
    {
      "venue": "kalshi",
      "url": "https://kalshi.com/markets/kxrecssnber/kxrecssnber-26",
      "rules_url": "https://assets.kalshi.com/contract_terms/NBERRECSS.pdf",
      "settlement_source": "Bureau of Economic Analysis",
      "excluded_reason": "rules",
      "match": {
        "differences": [
          "rules"
        ],
        "note": "Kalshi resolves on two negative GDP quarters only; Polymarket also counts an NBER recession call"
      },
      "volume_24h_usd": "123.64",
      "open_interest_usd": "17994.22"
    },
    {
      "venue": "polymarket",
      "url": "https://polymarket.com/event/us-recession-by-end-of-2026",
      "settlement_source": "Bureau of Economic Analysis; NBER",
      "weight": "1.0000",
      "volume_24h_usd": "2090.24"
    }
  ],
  "probability": "0.0550",
  "quotes": [
    {
      "venue": "kalshi",
      "probability": "0.0250",
      "market_ids": [
        "KXRECSSNBER-26"
      ]
    },
    {
      "venue": "polymarket",
      "probability": "0.0550",
      "market_ids": [
        "100379208559626151022751801118534484742123694725746262280150222742563282755057"
      ]
    }
  ]
}

probability 是“是”的市场赔率,这里为 0.0550,quotes 列出各市场的价格。Kalshi 的条目带有 excluded_reason: rules 和一条 match 说明:其规则只计入 GDP 连续两个季度为负,而 Polymarket 还计入研究机构作出的衰退判定。Kalshi 的 0.0250 仍保留在 quotes 中供参考,而顶层的 probability 等于 Polymarket 的报价。as_of 是报价时间,报价有效时 stale 为 false。

API 命名的四种形态

形态回答的问题需要读取的字段示例事件
scalar某次数据发布或决议会落在哪个数值或区间outcomes,每一项包含 key、label、lower、upper、probabilityusa-cpi-yoy-2026-11
binary会不会发生probability 和 quotesusa-recession-2026
timing会在何时之前发生steps,每一项包含 by、label、probabilityusa-fed-next-cut-to-2028-12-31
extreme某个序列会升到多高或降到多低由 level 和 probability 组成的 up 与 down 列表,外加 windowusa-unemployment-peak-2026

scalar 事件涵盖宏观仪表盘要展示的各项数据发布:CPI 的 12 个月变化、失业率、非农就业人数以及各央行的利率决议。CPI 或失业率事件的 resolves_with 字段指向用于结算它的 Narwhal 序列,例如 /v1/economics/USA/cpi。因此事件结算后,卡片可以从赔率直接链接到已发布的数字。

scalar 事件还会给区间的边界命名。在一个 outcome 中,lower 和 upper 限定区间范围,边界为 null 表示该侧是开区间,例如表示 3.0% 及以下的 le_3.0 一行。在为本页读取的 CPI、失业率和美联储事件中,各行的 probability 之和为 1,因此用 outcomes 画柱状图就能呈现完整的分布。

生成仪表盘的各行

脚本遍历三个分组,用各主题的形态所指向的端点读取该主题的下一个事件,并为每个卡片打印一行标题。scalar 显示可能性最大的结果,binary 显示“是”的赔率,timing 显示第一个节点,extreme 显示第一个水平。没有可用报价的卡片会如实说明。

python,每个主题一行标题
import json
import os
import urllib.request

def get(path):
    req = urllib.request.Request(
        "https://api.narwhalapi.com" + path,
        headers={"Authorization": "Bearer " + os.environ["NARWHAL_API_KEY"]},
    )
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r)

def headline(shape, e):
    if shape == "binary":
        if "probability" not in e:
            return "no usable quote"
        return f"yes {float(e['probability']):.1%}"
    if shape == "scalar":
        priced = [o for o in e["outcomes"] if "probability" in o]
        if not priced:
            return "no usable quote"
        top = max(priced, key=lambda o: float(o["probability"]))
        return f"{top['label']} {float(top['probability']):.1%}"
    if shape == "timing":
        step = e["steps"][0]
        if "probability" not in step:
            return "no usable quote"
        return f"by {step['label']} {float(step['probability']):.1%}"
    if shape == "extreme":
        if not e["up"] or "probability" not in e["up"][0]:
            return "no usable quote"
        step = e["up"][0]
        return f"reach {step['level']} {float(step['probability']):.1%}"

for group in ("us_prices", "us_jobs", "us_activity"):
    for topic in get("/v1/predictions/topics?group=" + group)["topics"]:
        if not topic.get("next_event_id") or topic["shape"] == "scalar" and topic["unit"] == "count":
            continue
        e = get(f"/v1/predictions/{topic['shape']}/{topic['next_event_id']}")
        print(group, e["event_id"], topic["shape"], headline(topic["shape"], e),
              "stale" if e["stale"] else "live")
事件形态标题
usa-cpi-yoy-2026-11标量3.9%, 20.0%
usa-unemployment-2026-10标量4.2%, 37.1%
usa-unemployment-peak-2026极值触及 5.0,3.1%
usa-recession-2026二元是 5.5%
usa-gdp-2026-q4标量无可用报价

这些行是某一次运行的输出。赔率会随市场变动,所以请查看每个卡片上的 as_of,并从 API 刷新页面。

没有可用报价的卡片是什么意思?

事件响应带有 stale: true,且没有概率字段。以 emu-ecb-cut-2026 为例,其 binary 响应的 quotes 列表为空,Polymarket 的条目带有 excluded_reason: thin。响应中没有可显示的概率,所以这类卡片应显示为不可用,而不是 0。

CPI、非农就业和失业率分别在哪些主题里?

usa-cpi-yoy、usa-cpi-mom、usa-core-cpi-yoy 和 usa-core-cpi-mom 在 us_prices 中。usa-payrolls 和 usa-unemployment 在 us_jobs 中。usa-recession-2026 在 us_activity 中,利率决议在 central_banks 中。先查询分组,再读取每个主题的 next_event_id。

如何列出即将到来的事件,而不是每个主题只取一个?

用 topic 和 status=trading 调用 /v1/predictions/events。对 usa-cpi-yoy,它会返回 2026 年 11 月的事件 usa-cpi-yoy-2026-11(在 2026 年 12 月 10 日 13:30 UTC 结算)以及 2026 年 12 月的事件 usa-cpi-yoy-2026-12。每一行都有 question、closes_at、resolves_at 和 shape。可以用它来做已有市场的数据发布日历视图,再用对应形态的端点打开每个事件。

同一个列表还接受 shape、from、to、limit 和 cursor,status=settled 则返回已经结算的事件。

如何展示利率决议卡片?

使用 central_banks 分组,或者调用某家央行的政策路径,即可在一个响应中得到每次已有定价的会议的降息、按兵不动和加息赔率。美联储的例子见如何解读美联储利率赔率。

发布于。

后续步骤