針對同一事件,篩查兩個預測市場之間的價差

在 Kalshi 與 Polymarket 之間篩查價差的交易台,需要同一事件的兩邊報價,以及這兩份合約是否按相同條件賠付。Narwhal 會在一次回應中回傳報價、各交易場所的規則文字、結算來源和成交量,以及任何規則標記。

在同一事件上讀取兩個交易場所

篩查從兩個交易場所都定了價的同一事件開始。下面的請求讀取 2026 年 12 月的聯準會決議,並按每個結果回傳各交易場所的報價。回應只保留升息 25 bps 這一結果,並省略了交易場所的規則文字,篩查會在下一步讀取它。

curl,兩個交易場所上的同一事件
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/predictions/scalar/usa-fed-2026-12"
回應,已精簡
{
  "event_id": "usa-fed-2026-12",
  "question": "Federal Reserve rate decision, 9 December 2026",
  "status": "trading",
  "closes_at": "2026-12-09T18:59:00Z",
  "resolves_at": "2026-12-09T19:00:00Z",
  "as_of": "2026-11-03T03:09:00Z",
  "stale": false,
  "venues": [
    {
      "venue": "kalshi",
      "url": "https://kalshi.com/markets/kxfeddecision/kxfeddecision-26dec",
      "rules_url": "https://assets.kalshi.com/contract_terms/FEDDECISION.pdf",
      "settlement_source": "Federal Reserve",
      "weight": "0.0624",
      "volume_24h_usd": "32792.43",
      "open_interest_usd": "426866.37"
    },
    {
      "venue": "polymarket",
      "url": "https://polymarket.com/event/fed-decision-in-december-20260729232808632",
      "settlement_source": "Federal Reserve",
      "weight": "0.9376",
      "volume_24h_usd": "665452.98"
    }
  ],
  "outcomes": [
    {
      "key": "hike_25",
      "label": "Hike of 25 bps",
      "lower": "12.5",
      "upper": "37.5",
      "probability": "0.7185",
      "quotes": [
        {
          "venue": "kalshi",
          "probability": "0.7073",
          "market_ids": [
            "KXFEDDECISION-26DEC-H25"
          ]
        },
        {
          "venue": "polymarket",
          "probability": "0.7192",
          "market_ids": [
            "111662129652828266889360374468657435151479595863413519836161861837506140152890"
          ]
        }
      ]
    }
  ]
}

該事件只有一個 as_of、一個 closes_at 和一個 resolves_at,所以兩邊報價取自同一時刻。對於升息這一結果,Kalshi 報價 0.7073,Polymarket 報價 0.7192,價差為 0.0119。每個交易場所項目都帶有 settlement_source 和 volume_24h_usd,Kalshi 還帶有 open_interest_usd。weight 表示各交易場所在整體 probability 中所佔的比重。

規則不同時,把價差當作兩份合約來解讀

價差只說明兩個數字不同,並不說明兩份合約的賠付條件相同。衰退問題就是回應自己做了標記的例子:該交易場所項目會帶有 excluded_reason 和一則說明差異所在的 match 備註。

curl,一個規則存在差異的二元問題
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/predictions/binary/usa-recession-2026"
回應,已精簡
{
  "event_id": "usa-recession-2026",
  "question": "US recession by end of 2026?",
  "as_of": "2026-11-03T03:09:00Z",
  "stale": false,
  "probability": "0.0550",
  "quotes": [
    {
      "venue": "kalshi",
      "probability": "0.0250",
      "market_ids": [
        "KXRECSSNBER-26"
      ]
    },
    {
      "venue": "polymarket",
      "probability": "0.0550",
      "market_ids": [
        "100379208559626151022751801118534484742123694725746262280150222742563282755057"
      ]
    }
  ],
  "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": "125.68",
      "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": "4195.50"
    }
  ]
}

Kalshi 報價 0.0250,Polymarket 報價 0.0550,價差為 0.0300。整體 probability 為 0.0550,這只是 Polymarket 的報價,因為 Kalshi 因 rules 被排除。備註說明,Kalshi 只在 GDP 連續兩個季度為負時才結算,而 Polymarket 還會把研究機構作出的衰退判定計入。兩邊的結算來源也不同。篩查應當把這個價差從候選清單中剔除,或標記為不同的合約。

篩查每個結果並寫入一列日誌

下面的腳本讀取一份事件清單,對兩個交易場所都有報價的每個結果,計算 Polymarket 減去 Kalshi 的價差,並為每個結果列印一列,附上交易台用來篩選的標記。

Python,跨多個事件的一次掃描
import csv, json, os, sys, urllib.request
from decimal import Decimal

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 scan(shape, event_id):
    e = get(f"/v1/predictions/{shape}/{event_id}")
    venues = {v["venue"]: v for v in e["venues"]}
    flags = [v["venue"] + ":" + ",".join(v.get("match", {}).get("differences", [])) for v in e["venues"] if v.get("excluded_reason")]
    sources = {v["settlement_source"] for v in e["venues"]}
    rows = e["outcomes"] if shape == "scalar" else [{"key": "yes", "quotes": e["quotes"]}]
    for row in rows:
        q = {x["venue"]: Decimal(x["probability"]) for x in row["quotes"]}
        if len(q) < 2:
            continue
        yield [e["as_of"], e["stale"], event_id, row["key"], q["kalshi"], q["polymarket"],
               q["polymarket"] - q["kalshi"], len(sources), ";".join(flags) or "none",
               venues["kalshi"]["volume_24h_usd"], venues["polymarket"]["volume_24h_usd"]]

out = csv.writer(sys.stdout)
out.writerow(["as_of", "stale", "event", "outcome", "kalshi", "polymarket", "gap", "sources", "flags", "k_vol_24h", "p_vol_24h"])
for shape, event_id in [("scalar", "usa-fed-2026-12"), ("binary", "usa-recession-2026")]:
    out.writerows(scan(shape, event_id))
產出
as_of,stale,event,outcome,kalshi,polymarket,gap,sources,flags,k_vol_24h,p_vol_24h
2026-11-03T03:09:00Z,False,usa-fed-2026-12,cut_50_plus,0.0049,0.0035,-0.0014,1,none,32792.43,665452.98
2026-11-03T03:09:00Z,False,usa-fed-2026-12,cut_25,0.0146,0.0055,-0.0091,1,none,32792.43,665452.98
2026-11-03T03:09:00Z,False,usa-fed-2026-12,hold,0.2586,0.2530,-0.0056,1,none,32792.43,665452.98
2026-11-03T03:09:00Z,False,usa-fed-2026-12,hike_25,0.7073,0.7192,0.0119,1,none,32792.43,665452.98
2026-11-03T03:09:00Z,False,usa-fed-2026-12,hike_50_plus,0.0146,0.0188,0.0042,1,none,32792.43,665452.98
2026-11-03T03:09:00Z,False,usa-recession-2026,yes,0.0250,0.0550,0.0300,2,kalshi:rules,125.68,4195.50

五個 Fed 列共用同一個結算來源且沒有標記,因此在結算上可以比較。每個交易場所的五個報價加起來為 1.0000,所以價差之和為零。衰退列有兩個結算來源,並帶有 kalshi:rules 標記。規則措辭仍然需要人工或 diff 來核對:Kalshi 的文字稱,會議取消時「維持不變」這一結果按 Yes 結算;而 Polymarket 的文字則把變動到不在清單上的水準向上進位到最接近的 25 bps,並且在下一次例會結束前仍無聲明時,按「維持不變」區間結算。

每次掃描要記錄什麼

掃描只有在日後可以重播時才有用。請把輸入與價差一併記錄下來。

欄位記錄原因
as_of 和 stale兩邊報價的時間,以及事件資料是否為最新
event_id 和結果的 key所比較的確切合約對
兩邊報價和價差數值,以字串或小數形式保存
每個報價的 market_ids每個數字背後的交易場所合約
excluded_reason 和 match價差為何屬於不同的合約
各交易場所的 settlement_source結果是否由同一機構決定
volume_24h_usd, open_interest_usd報價背後有多少交易量支撐
每份 rules_text 的雜湊值兩次掃描之間的規則修改會被發現

Fed 的例子說明了為什麼要記錄規模。在這次掃描中,Kalshi 的 24 小時成交量是 32,792.43 美元,Polymarket 的是 665,452.98,同樣的價差背後的深度相差懸殊。衰退列中 Kalshi 的成交量為 125.68。

出現價差是否意味著可以按這些價格成交?

API 並未這樣表示,本頁也不對收益作任何主張。報價是機率,不是委託單。回應裡沒有費率表,沒有委託簿深度,也沒有交易台可以成交的價格。篩查只是把需要細讀的事件清單縮小。

各交易場所的權重能告訴交易台什麼?

weight 是各交易場所在整體 probability 中所佔的份額,各權重之和為 1。在這次掃描中,Kalshi 為 0.0624,Polymarket 為 0.9376。把它們套用到維持不變的報價上得到 0.25335,而整體值為 0.2533。某個交易場所的權重很大,意味著整體值主要跟隨該場所,所以比較報價的篩查應當使用各交易場所自己的報價,而不是整體值。

如果交易場所對結果的劃分不同怎麼辦?

交易場所項目會在 match.differences 中報告 outcome_set,並在 match 備註中說明合併方式。此時 quotes 中的報價已經對齊到同一列。在按標籤比較兩份結果清單之前,請先閱讀 match。針對單一問題的步驟,見指南比較 Polymarket 與 Kalshi。

各交易場所的收盤時間和結算時間是否不同?

事件只給出一個 closes_at 和一個 resolves_at。各交易場所關於時間的措辭寫在 rules_text 中,所以關心這一點的篩查要比較兩份文字。該事件的 Polymarket 文字把 FOMC 聲明列為結算來源,並規定若未發布聲明則回退到「維持不變」區間。

發布於 。

後續步驟