Kalshi와 Polymarket 사이의 가격 차이를 찾는 데스크에는 같은 이벤트에 대한 두 호가와, 두 계약이 같은 조건으로 지급되는지가 필요합니다. Narwhal은 호가, 각 거래소의 규칙 문구, 정산 출처, 거래량, 그리고 규칙 관련 플래그를 하나의 응답으로 반환합니다.
한 이벤트에서 두 거래소 읽기
스크리닝은 두 거래소가 모두 가격을 매긴 하나의 이벤트에서 시작합니다. 아래 요청은 2026년 12월 연방준비제도 결정을 조회하고 결과마다 모든 거래소의 호가를 반환합니다. 응답은 25 bps 인상 결과만 남기고 거래소 규칙 문구는 생략했으며, 규칙 문구는 다음 단계에서 읽습니다.
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 -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를 뺀 차이를 계산한 뒤, 데스크가 필터링에 쓰는 플래그와 함께 결과당 한 행을 출력합니다.
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.50Fed의 다섯 행은 정산 출처가 하나이고 플래그가 없으므로 정산 면에서 비교할 수 있습니다. 각 거래소의 다섯 호가는 합계가 1.0000이므로 차이의 합은 0입니다. 경기 침체 행에는 정산 출처가 두 개이고 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 성명을 지정하고, 성명이 나오지 않으면 변동 없음 구간으로 대체한다고 정합니다.
에 게시되었습니다.