同じイベントについて、2つの予測市場の価格差をスクリーニングする

Kalshi と Polymarket の価格差を探すデスクには、同じイベントの両方の気配値に加えて、2つの契約が同じ条件で支払われるかどうかが必要です。Narwhal は、気配値、各取引所のルール文、決済の参照元、出来高、およびルールに関するフラグを1つのレスポンスで返します。

1つのイベントで両方の取引所を読む

スクリーニングは、両方の取引所が値付けしている1つのイベントから始まります。以下のリクエストは2026年12月の連邦準備制度の決定を読み取り、結果ごとにすべての取引所の気配値を返します。レスポンスは25 bpsの利上げという結果だけに絞り、取引所のルール文は省いています。ルール文は次のステップで読み取ります。

curl、2つの取引所での1つのイベント
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 が1つずつあるため、両方の気配値は同じ時点で取得されています。利上げという結果では、Kalshi の気配値は 0.7073、Polymarket は 0.7192 で、差は 0.0119 です。各取引所のエントリには settlement_source と volume_24h_usd があり、Kalshi には open_interest_usd もあります。weight は、各取引所が代表値の probability にどれだけ寄与するかを表します。

ルールが異なるときは、価格差を別々の2つの契約として読む

価格差が示すのは、2つの数値が異なるということだけです。2つの契約が同じ条件で支払われるとは示しません。景気後退の質問は、レスポンス自身がフラグを立てる例です。取引所のエントリに 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が2四半期連続でマイナスの場合だけを成立条件とするのに対し、Polymarket は調査機関による景気後退の判定も数えます。決済の参照元も異なります。スクリーニングでは、この価格差を候補リストから外すか、別の契約として印を付けるべきです。

すべての結果をスクリーニングし、ログの行を書き出す

以下のスクリプトはイベントのリストを読み取り、両方の取引所が気配値を出している結果ごとに Polymarket から Kalshi を引いた差を計算し、デスクが絞り込みに使うフラグとともに、結果1件につき1行を出力します。

Python、複数イベントにわたる1回のスキャン
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 の5行は決済の参照元が1つで、フラグもないため、決済の面で比較できます。各取引所の5つの気配値は合計 1.0000 になるので、価格差の合計はゼロです。景気後退の行には決済の参照元が2つあり、kalshi:rules のフラグが付いています。ルールの文言は、やはり人間か差分での確認が必要です。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 のハッシュ2回のスキャンの間にルールが編集されたことが見える

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 の気配値は、その時点ですでに同じ行にそろっています。2つの結果リストをラベルで比べる前に、match を読んでください。1つの質問についての手順は、Polymarket と Kalshi を比較するガイドにあります。

締め切りと決済の時刻は取引所ごとに違いますか。

イベントには closes_at と resolves_at が1つずつあります。時刻に関する取引所ごとの文言は rules_text にあるため、それを気にするスクリーニングでは2つの文面を比較します。このイベントの Polymarket の文面は、決済の参照元として FOMC 声明を挙げ、声明が出ない場合は変更なしの区分に戻ると定めています。

公開。

次のステップ