syntax = "proto3"; package kairos.v1; import "kairos/v1/common.proto"; option go_package = "github.com/kairos/proto/kairos/v1"; // ArbMatch represents a matched market pair across two providers. message ArbMatch { Provider a_provider = 1; string a_market_id = 2; string a_name = 3; string a_label = 4; // Price scaled by 10000 (e.g., 4200 = 0.42) int32 a_price_yes = 5; // Expiry in seconds since Unix epoch (0 = no expiry) int64 a_expires_at_sec = 6; Provider b_provider = 7; string b_market_id = 8; string b_name = 9; string b_label = 10; int32 b_price_yes = 11; int64 b_expires_at_sec = 12; int32 spread_bps = 13; string event_title = 14; string clean_title = 15; // Similarity scaled by 10000 (e.g., 9500 = 0.95). Unverified fail-open // candidates use zero rather than being represented as perfect matches. int32 similarity_scaled = 16; // Liquidity-aware executable sizing, computed server-side by arb-scanner // walking both legs' L2 books in lockstep (buy YES on A + buy NO on B). // Absent (nil) when the books are unavailable/stale for this pair. ArbLiquidity liquidity = 17; // Maximum currently executable arb notional in dollars, scaled by 10000. // This is an explicit top-level copy of liquidity.executable_notional_scaled // so consumers can filter/sort without decoding the depth ladder. int64 max_arb_liquidity_scaled = 18; } // Liquidity-aware sizing computed server-side by arb-scanner walking both // legs' books in lockstep (see services/arb-scanner/scanner/depth_walk.go). message ArbDepthStep { int32 yes_price = 1; // scaled by 10000 int32 no_price = 2; // scaled by 10000 int32 blended_cost = 3; // scaled by 10000 int32 edge = 4; // scaled by 10000 (can be <= 0 on the floor row) double contracts = 5; // contracts filled at this step (fractional sizes exist on Polymarket) double cumulative_contracts = 6; int64 cumulative_cost_scaled = 7; // dollars scaled by 10000 int64 cumulative_profit_scaled = 8; // dollars scaled by 10000 bool viable = 9; } message ArbLiquidity { double executable_contracts = 1; int64 executable_notional_scaled = 2; // dollars scaled by 10000 int64 locked_profit_scaled = 3; // dollars scaled by 10000 int32 avg_blended_cost = 4; // scaled by 10000 int32 top_of_book_edge = 5; // scaled by 10000 repeated ArbDepthStep ladder = 6; string exhausted = 7; // edge|yesBook|noBook|levels int64 computed_at_us = 8; } // Full set of matched market pairs. // Wire: [0x0D][protobuf payload] // Redis channel: arb:matches message ArbMatchList { int64 timestamp_us = 1; uint32 count = 2; repeated ArbMatch matches = 3; string subscription_id = 4; } // Live price divergence for a matched pair. message ArbSpread { Provider a_provider = 1; string a_market_id = 2; int32 a_price_yes = 3; int64 a_expires_at_sec = 4; Provider b_provider = 5; string b_market_id = 6; int32 b_price_yes = 7; int64 b_expires_at_sec = 8; int32 spread_bps = 9; string direction = 10; string title = 11; string clean_title = 12; // Liquidity-aware executable sizing for this pair (buy YES on A + buy NO // on B), computed server-side by arb-scanner. Absent (nil) when the books // are unavailable/stale. ArbLiquidity liquidity = 13; // Maximum currently executable arb notional in dollars, scaled by 10000. int64 max_arb_liquidity_scaled = 14; // Match confidence scaled by 10000 so subscription filters can be applied // to spread batches without joining against a separate match snapshot. // Unverified fail-open candidates use zero. int32 similarity_scaled = 15; } // Authoritative snapshot of currently active, edge-gated opportunities. // Consumers replace local state on every batch, including an empty batch. // Wire: [0x0E][protobuf payload] // Redis channel: arb:spreads message ArbSpreadList { int64 timestamp_us = 1; uint32 count = 2; repeated ArbSpread opportunities = 3; string subscription_id = 4; }