Skip to content
← ブログ

KalshiとPolymarketを安全に比較する

dino.markets

KalshiとPolymarketの比較は、契約から始まります。2つの掲載は平易な英語で同じイベントを説明していても、異なるoutcome、観測期間、データソース、境界に基づいて支払われることがあります。これらの規約を確認する前に価格を並べると、実際には異なる2つの主張を、精密に見えるだけの比較にしてしまいます。

Dinoは関連する掲載を1つのMarketオブジェクトにマッチングし、解決の違いをそのオブジェクトに紐づけて保持します。このチュートリアルではPython SDKを使って1つのペアを確認し、契約のチェックを終えた後にのみ価格差を計算します。

マッチング済みMarketとその詳細を取得する

無料APIキーを作成し、クライアントをインストールします。次に、オープン中のmarketファミリーをリクエストします。この例では、観測地点と気温区分の違いが開示内容から確認しやすいため、天気を使います。

export DINO_API_KEY=sk_live_...
pip install dino-markets
from dino_markets import Dino

client = Dino()
catalog = client.markets(
    category="weather",
    market_type="weather_high",
    status="open,live",
    limit=20,
)

summary = next(
    (
        market
        for market in catalog["markets"]
        if market["coverage"]["kalshi"]
        and market["coverage"]["polymarket"]
    ),
    None,
)
if summary is None:
    raise SystemExit("No two-venue weather comparison is available right now.")

market = client.market(summary["id"])

リスト結果は、安定したDinoの id と現在の遅延価格を提供します。client.market(id) は、完全な比較のために正規化されたコンテキスト、venueの識別情報、板の厚み、解決の開示内容を追加します。

開示内容をレビューチェックリストに変換する

次の関数は、価格計算を行う前に4つのチェックを可視化します。

def comparison_checks(market):
    settlement = market.get("settlement") or {}
    risks = settlement.get("risks") or []
    served_blocking = [
        risk
        for risk in risks
        if risk.get("severity") == "high"
    ]
    fully_priced = bool(market["outcomes"]) and all(
        outcome.get("kalshi") is not None
        and outcome.get("polymarket") is not None
        for outcome in market["outcomes"]
    )

    return {
        "entity_match_served": market["match"] == "high_confidence",
        "full_price_coverage": fully_priced,
        "settlement_parity": settlement.get("parity") is True,
        "served_blocking_risks": served_blocking,
    }

checks = comparison_checks(market)
print(checks)

match は同一性の問いに答えます。Dinoが、両方の掲載を同じイベントまたは根底にある主張を表すものとして一緒に提供するだけの十分な根拠を持っているかどうかです。価格の完全なカバレッジは、すべてのoutcomeが両venueで比較可能かどうかを示します。解決のparityと提供されるリスクリストは、以下のキャンセルに関する開示を前提として、マッチングされたoutcomeが両立可能な条件で支払われるかどうかを示します。

Marketは、解決に関する留保事項を伴いながらも、有効なvenue間比較となり得ます。その場合、signalspread のままであり、その留保事項は価格の隣に置かれるべきです。確定済みのOpportunityはより高いゲートを持ち、別の /v2/arbitrage リソースから得られます。

契約を層ごとに読む

Marketの詳細フィールドを、決まったレビュー順序として使います。

確認項目フィールド確認すること
同一性match, title, カテゴリ関連の情報両venueの掲載が同じイベントまたは主張を指しているか。
outcomeoutcomes[].key, outcomes[].name区分が同じ現実世界の結果をカバーしているか。
カバレッジcoverage, outcomeの価格比較が必要な箇所すべてで、両venueに実際の価格があるか。
解決settlement.parity, settlement.risks[], settlement.kalshi_text, settlement.poly_text解決のソースと締め切りの境界が両立可能か、開示されているか。キャンセルの文言は各venueの規約テキストを確認する。

提供される settlement.risks[] は、内部の cancel_* イベントを意図的に除外しています。各venueのキャンセル条件については kalshi_textpoly_text を確認してください。Dinoは、完全な内部リスクセットから settlement.paritysignal を計算した後、それらのイベントを公開リスクリストから除外します。

天気のmarketは、各層がなぜ重要かを示す例です。2つの日次最高気温の契約は、同じ都市名を使っていても、異なる観測地点から解決されることがあります。気温の区分が1度ずれている場合もあります。Dinoは各venueの実際の区分を保持し、統合した価格を作り出す代わりに重なりを報告します。

暗号資産の閾値marketでは、別の境界の問題が生じます。一方のvenueは「以上」で解決し、もう一方は「超」を使うことがあります。閾値ちょうどでの価格差は、両方のタイトルが同じ資産と数値に言及していても、異なる勝ち筋の契約を指すことになります。

価格差の計算は最後に行う

開示内容の確認が終わったら、価格が揃っているoutcomeごとに観測された差を計算します。

for outcome in market["outcomes"]:
    kalshi = outcome.get("kalshi")
    polymarket = outcome.get("polymarket")
    if kalshi is None or polymarket is None:
        continue

    gap_pts = abs(kalshi - polymarket) * 100
    print(
        outcome["name"],
        f"Kalshi {kalshi:.2f}",
        f"Polymarket {polymarket:.2f}",
        f"gap {gap_pts:.1f} pts",
    )

結果は、Marketの priced_at タイムスタンプにおけるvenue間のスプレッドです。これは、観測された2つの価格がどれだけ離れているかを示すものです。実際の執行には、現在のオーダーブックのデータ、利用可能なサイズ、手数料、両立可能な解決条件が必要です。これは開発者向けデータであり、投資助言や取引助言ではありません。

同一性と発動ゲートについてはDinoがmarketをマッチングする仕組みを、上記で使われたすべてのフィールドについてはMarketオブジェクトリファレンスを参照してください。ライブボードでは、同じ開示内容を人が読みやすい形式で確認できます。

ブログの他の記事