// Code generated by cargo codegen from the 0xinsider OpenAPI document. DO NOT EDIT.
//! Typed models for every schema the OpenAPI document names, and for the request
//! and response bodies it defines inline.
//!
//! A field the document marks optional or nullable is an `Option`: `None` means the
//! API did not report that value, which is not a zero. Unknown JSON keys are ignored
//! and an unknown enum value lands in `Other`, so an additive API change never fails
//! a response. Money and price fields are `f64`, the precision the API sends.
// Descriptions are the document's own, quoted verbatim; a field it leaves
// undescribed has no doc comment rather than an invented one.
#![allow(missing_docs)]
#![allow(rustdoc::bare_urls, rustdoc::broken_intra_doc_links, rustdoc::invalid_html_tags)]
#![allow(clippy::doc_lazy_continuation, clippy::large_enum_variant)]
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccountIdentity {
pub object: String,
pub data: AccountIdentityData,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccountIdentityData {
pub user_id: i64,
pub credential_id: i64,
pub credential_kind: CredentialKind,
/// The credential is currently valid; revoked, expired or unknown credentials are rejected.
pub credential_status: String,
pub entitlement: AccountIdentityDataEntitlement,
/// Approved OAuth scopes; null for full developer-key access.
#[serde(default)]
pub scopes: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccountIdentityDataEntitlement {
pub paid_data_access: PaidDataAccess,
/// When paid data access is lapsed, the caller should renew the subscription; otherwise null.
#[serde(default)]
pub recovery_action: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Action {
/// `buy`
#[serde(rename = "buy")]
Buy,
/// `sell`
#[serde(rename = "sell")]
Sell,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Action {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Buy => "buy",
Self::Sell => "sell",
Self::Other(value) => value,
}
}
}
impl From<&str> for Action {
fn from(value: &str) -> Self {
match value {
"buy" => Self::Buy,
"sell" => Self::Sell,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Action {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Action {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ActivateWebhookSecretResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum AdmittedBy {
/// `most_held`
#[serde(rename = "most_held")]
MostHeld,
/// `soonest_kickoff`
#[serde(rename = "soonest_kickoff")]
SoonestKickoff,
/// `most_held_48h`
#[serde(rename = "most_held_48h")]
MostHeld48h,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl AdmittedBy {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::MostHeld => "most_held",
Self::SoonestKickoff => "soonest_kickoff",
Self::MostHeld48h => "most_held_48h",
Self::Other(value) => value,
}
}
}
impl From<&str> for AdmittedBy {
fn from(value: &str) -> Self {
match value {
"most_held" => Self::MostHeld,
"soonest_kickoff" => Self::SoonestKickoff,
"most_held_48h" => Self::MostHeld48h,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for AdmittedBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for AdmittedBy {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A sandbox key and the path to live access (#13959). Nothing is stored: the key cannot be listed or revoked and does not expire. Register again for a new one.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AgentRegistration {
/// The sandbox key. Send it as Authorization: Bearer <api_key> to the sandbox. The last 8 hex characters are a checksum (the first 4 bytes of SHA-256 over the rest of the key), so the sandbox can tell a mistyped key from a real one. It is not a secret and unlocks no production data.
pub api_key: String,
/// Always false: this key never reaches live data.
pub livemode: bool,
pub environment: String,
pub created_at: String,
pub sandbox: AgentRegistrationSandbox,
/// What live data needs, and where each credential comes from. Both need a person: an account with an active Pro subscription.
pub live_access: AgentRegistrationLiveAccess,
}
/// What live data needs, and where each credential comes from. Both need a person: an account with an active Pro subscription.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AgentRegistrationLiveAccess {
/// The production V1 base URL.
pub api_base_url: String,
/// What a live credential needs, in one sentence.
pub requirement: String,
/// Where a signed-in Pro user creates a live key (oxi_sk_live_).
pub api_keys_url: String,
/// RFC 8414 authorization server metadata, for an OAuth 2.1 grant the user approves.
pub oauth_authorization_server_metadata_url: String,
/// RFC 7591 dynamic client registration for public OAuth clients.
pub oauth_registration_endpoint: String,
/// The Pro plan.
pub pricing_url: String,
/// The authentication walkthrough for agents.
pub auth_guide_url: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AgentRegistrationSandbox {
/// The sandbox V1 base URL. Every documented operation answers here with example data, except GET /api/v1/stream: a Server-Sent Events stream is a live connection rather than a body, so the sandbox answers it with 400.
pub api_base_url: String,
/// A read to send with the key.
pub first_request_url: String,
/// The OpenAPI document, served by the sandbox.
pub openapi_url: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ApiDiscovery {
/// Canonical API origin for public V1 requests.
pub api_base_url: String,
/// Full agent-readable API reference.
pub docs_url: String,
/// Canonical web-origin OpenAPI JSON document.
pub openapi_url: String,
/// Unauthenticated API health endpoint.
pub health_url: String,
pub authentication: String,
/// RFC 9728 protected-resource metadata for the API origin: the document every V1 401 names in its WWW-Authenticate challenge (resource, bearer_methods_supported, resource_documentation). The remote MCP server has its own document at /.well-known/oauth-protected-resource/api/v1/mcp.
pub protected_resource_metadata_url: String,
/// The complete authenticated route index: one entry per authenticated route this spec documents, in "<METHOD> <path>" form, not a representative subset. GET /api/v1 is the unauthenticated entrypoint an agent hits first, so it hands back the whole authenticated surface rather than a sample the caller would have to guess around. The example on GET /api/v1 is abridged for readability -- the live response returns all of them. Kept in lockstep with this spec by review: it is a hand-maintained const in backend/src/api_v1/discovery.rs and no automated check compares the two, so treat this spec as authoritative if they ever disagree.
pub authenticated_routes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ApiError {
pub object: String,
pub error: ApiErrorError,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ApiErrorBody {
/// FROZEN: an existing value never changes meaning. request_timeout (408, #16146) was added the way insufficient_scope was: the handler did not answer inside the server's 30-second timeout. Retry-After and retry_at ride on it only for a safe method (GET, HEAD); a timed-out mutation may have completed, so check its state and reuse its Idempotency-Key.
pub code: ErrorCode,
pub message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub doc_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub param: Option<String>,
/// The recommended next retry instant (RFC3339). Present on every retryable error (reason=pick_not_released, code=rate_limited including reason=monthly_quota_exceeded, code=rate_limit_unavailable, reason=read_model_warming) and omitted otherwise. Always in the future. For pick_not_released, the recommendation is advisory and can change before a pick becomes available. Schedule the next request from the returned value instead of inferring a release schedule or polling. Prefer Retry-After for the duration because it is immune to client clock skew.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retry_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub freshness: Option<FreshnessFailure>,
/// ADDITIVE (#7209). The specific, actionable cause behind `code`, when there is one more specific than the code itself. `code` keeps its published values, so existing clients are unaffected; new clients branch on `reason`. Omitted when the code already says everything we know. pick_not_released: no Pick of the Day is published for the current product day; schedule one request against retry_at instead of polling. unknown_endpoint: the PATH is not a route on this API -- read GET /api/v1, do not retry. trader_not_tracked: the wallet is real and the URL is right, but the trader is not on the warm sync tier (the wallets we sync most often) -- stop asking for this wallet. market_out_of_scope (404 not_found, #27303): the market or event is real and the URL is right, but 0xinsider serves sports and esports markets only -- stop asking for this market; neither a retry nor another URL for it can succeed. cursor_expired: pagination went stale mid-walk -- re-request the first page and continue. read_model_warming: the requested endpoint cannot serve its read model yet; exact causes are endpoint-specific and can include a cold or contended refresh or a dependency that prevented refresh. database_unavailable: the API's database or its connection pool is temporarily unreachable (a connection-class failure, not a query fault); code stays rate_limit_unavailable, nothing is rate-limited, retry after Retry-After / retry_at. idempotency_in_progress: retain the exact Idempotency-Key and request body, then retry shortly. webhook_delivery_in_progress: retry the URL or signing-secret configuration change after the destination's active request completes. request_accounting_unavailable: accounting capacity is unavailable before the handler executes; retry after Retry-After / retry_at. sandbox_api_key: the credential is a sandbox key (oxi_sk_test_) from POST /api/v1/agents/register, which only the sandbox server accepts -- call the sandbox base URL with it, or get a live key or OAuth access token; do not retry it here. api_key_in_query: the key was sent as a ?token= query parameter, which no route reads because URLs land in logs and history; the key itself was not checked -- resend it as Authorization: Bearer. subscription_inactive: the key is valid but the account's Pro subscription has lapsed (402 subscription_required); permanent until a person reactivates at https://0xinsider.com/billing, which the message names -- stop retrying on a schedule and surface the link. The key owner is emailed once per lapse. monthly_quota_exceeded: the account has used the requests its plan includes for the UTC calendar month (429 rate_limited); retry_at and Retry-After name the first of next month, the only retry that can succeed, and the message names https://0xinsider.com/developers, where pay as you go for requests over the quota is turned on. The X-Monthly-Quota-Limit, X-Monthly-Quota-Remaining and X-Monthly-Quota-Reset headers on every authenticated response say how close the account is. invalid_query, invalid_path, invalid_body (400 bad_request, #16146): a query parameter, a path segment or the JSON body did not parse or does not fit the route's schema, so no handler ran; param names the field when the parser named one (a query key, a path segment, a JSON path such as traders[0], or body); fix the request, never retry it as sent. unsupported_media_type (415 bad_request, param content-type): send the body with Content-Type: application/json. payload_too_large (413 bad_request, param body): the body is over 1048576 bytes. method_not_allowed (405 bad_request): the path is a route but not with this method; the Allow header names the methods it serves. ip_rate_limited (429 rate_limited, #16380): the per-address budget every caller behind one IP shares, counted before authentication, is spent; not the key's own window, and the RateLimit-* headers describe that bucket. ip_throttled (429 rate_limited): the address is in a cooldown after sustained over-limit traffic; Retry-After is minutes to days, and a request before it does not shorten the cooldown. max_plan_required (403 forbidden, #23085): the key is valid and its account is on Pro, but the route is part of Max (GET /api/v1/edge-hunter); every Pro route keeps working with the same key, so do not disable it. The message names https://0xinsider.com/pricing, where the account upgrades; a retry never clears it. endpoint_retired (410): this legacy sports endpoint no longer serves data. Migrate to GET /api/v1/edge-hunter; retrying the retired path cannot succeed.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<ErrorReason>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ApiErrorError {
/// FROZEN: an existing value never changes meaning. request_timeout (408, #16146) was added the way insufficient_scope was: the handler did not answer inside the server's 30-second timeout. Retry-After and retry_at ride on it only for a safe method (GET, HEAD); a timed-out mutation may have completed, so check its state and reuse its Idempotency-Key.
pub code: ErrorCode,
pub message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub doc_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub param: Option<String>,
/// The recommended next request instant (RFC3339), always in the future. Present on every retryable error: `pick_not_released`, `rate_limited`, `rate_limit_unavailable`, and `read_model_warming`. Omitted otherwise. The absolute twin of `Retry-After`; prefer the header for the sleep duration. For `pick_not_released`, the earliest of the next scheduled release, the next automatic selector attempt, the operating-window start, or about 60 seconds. See that response.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retry_at: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub freshness: Option<FreshnessFailure>,
/// ADDITIVE (#7209). The specific, actionable cause behind `code`, when there is one more specific than the code itself. `code` keeps its published values, so existing clients are unaffected; new clients branch on `reason`. Omitted when the code already says everything we know. pick_not_released: no Pick of the Day is published for the current product day; schedule one request against retry_at instead of polling. unknown_endpoint: the PATH is not a route on this API -- read GET /api/v1, do not retry. trader_not_tracked: the wallet is real and the URL is right, but the trader is not on the warm sync tier (the wallets we sync most often) -- stop asking for this wallet. market_out_of_scope (404 not_found, #27303): the market or event is real and the URL is right, but 0xinsider serves sports and esports markets only -- stop asking for this market; neither a retry nor another URL for it can succeed. cursor_expired: pagination went stale mid-walk -- re-request the first page and continue. read_model_warming: the requested endpoint cannot serve its read model yet; exact causes are endpoint-specific and can include a cold or contended refresh or a dependency that prevented refresh. database_unavailable: the API's database or its connection pool is temporarily unreachable (a connection-class failure, not a query fault); code stays rate_limit_unavailable, nothing is rate-limited, retry after Retry-After / retry_at. idempotency_in_progress: retain the exact Idempotency-Key and request body, then retry shortly. webhook_delivery_in_progress: retry the URL or signing-secret configuration change after the destination's active request completes. request_accounting_unavailable: accounting capacity is unavailable before the handler executes; retry after Retry-After / retry_at. sandbox_api_key: the credential is a sandbox key (oxi_sk_test_) from POST /api/v1/agents/register, which only the sandbox server accepts -- call the sandbox base URL with it, or get a live key or OAuth access token; do not retry it here. api_key_in_query: the key was sent as a ?token= query parameter, which no route reads because URLs land in logs and history; the key itself was not checked -- resend it as Authorization: Bearer. subscription_inactive: the key is valid but the account's Pro subscription has lapsed (402 subscription_required); permanent until a person reactivates at https://0xinsider.com/billing, which the message names -- stop retrying on a schedule and surface the link. The key owner is emailed once per lapse. monthly_quota_exceeded: the account has used the requests its plan includes for the UTC calendar month (429 rate_limited); retry_at and Retry-After name the first of next month, the only retry that can succeed, and the message names https://0xinsider.com/developers, where pay as you go for requests over the quota is turned on. The X-Monthly-Quota-Limit, X-Monthly-Quota-Remaining and X-Monthly-Quota-Reset headers on every authenticated response say how close the account is. invalid_query, invalid_path, invalid_body (400 bad_request, #16146): a query parameter, a path segment or the JSON body did not parse or does not fit the route's schema, so no handler ran; param names the field when the parser named one (a query key, a path segment, a JSON path such as traders[0], or body); fix the request, never retry it as sent. unsupported_media_type (415 bad_request, param content-type): send the body with Content-Type: application/json. payload_too_large (413 bad_request, param body): the body is over 1048576 bytes. method_not_allowed (405 bad_request): the path is a route but not with this method; the Allow header names the methods it serves. ip_rate_limited (429 rate_limited, #16380): the per-address budget every caller behind one IP shares, counted before authentication, is spent; not the key's own window, and the RateLimit-* headers describe that bucket. ip_throttled (429 rate_limited): the address is in a cooldown after sustained over-limit traffic; Retry-After is minutes to days, and a request before it does not shorten the cooldown. max_plan_required (403 forbidden, #23085): the key is valid and its account is on Pro, but the route is part of Max (GET /api/v1/edge-hunter); every Pro route keeps working with the same key, so do not disable it. The message names https://0xinsider.com/pricing, where the account upgrades; a retry never clears it. endpoint_retired (410): this legacy sports endpoint no longer serves data. Migrate to GET /api/v1/edge-hunter; retrying the retired path cannot succeed.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<ErrorReason>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Basis {
/// `midpoint`
#[serde(rename = "midpoint")]
Midpoint,
/// `last_trade`
#[serde(rename = "last_trade")]
LastTrade,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Basis {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Midpoint => "midpoint",
Self::LastTrade => "last_trade",
Self::Other(value) => value,
}
}
}
impl From<&str> for Basis {
fn from(value: &str) -> Self {
match value {
"midpoint" => Self::Midpoint,
"last_trade" => Self::LastTrade,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Basis {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Basis {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchGetMarketFlowBody {
/// Market condition IDs to resolve in input order.
pub condition_ids: Vec<String>,
/// Lookback window used for flow and trade-count context.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeframe: Option<Timeframe>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct BatchGetMarketFlowResponse {
pub object: String,
pub data: Vec<BatchMarketFlowItem>,
pub meta: BatchResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchGetMarketIntelBody {
/// Market condition IDs to resolve in input order.
pub condition_ids: Vec<String>,
/// Lookback window used for flow and trade-count context.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeframe: Option<Timeframe>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct BatchGetMarketIntelResponse {
pub object: String,
pub data: Vec<BatchMarketFlowItem>,
pub meta: BatchResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchGetTradersBody {
/// Trader identities to resolve in input order.
pub traders: Vec<String>,
/// Shared expand flags applied to every trader item.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expand: Option<Vec<Expand>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct BatchGetTradersResponse {
pub object: String,
pub data: Vec<BatchTraderItem>,
pub meta: BatchResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct BatchMarketFlowItem {
/// Zero-based request index. Duplicate inputs keep separate result rows.
pub index: i64,
pub input: String,
pub status: BatchTraderItemStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<MarketFlow>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<ApiErrorBody>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct BatchRateLimitMeta {
pub basis: String,
/// The batch item budget per minute for the caller's plan: 2500 item units on Pro, 5000 on Max. The same number X-Batch-RateLimit-Limit reports.
pub limit: i64,
pub remaining: i64,
/// Unix timestamp when the batch item window resets.
pub reset: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct BatchResponseMeta {
/// Unique request ID (req_ prefix). The same value as the X-Request-Id response header, the request's usage accounting row and its log lines.
pub request_id: String,
pub cached: bool,
pub total_items: i64,
pub successful_items: i64,
pub failed_items: i64,
/// Number of batch item units reserved before execution.
pub request_cost: i64,
pub rate_limit: BatchRateLimitMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct BatchTraderItem {
/// Zero-based request index. Duplicate inputs keep separate result rows.
pub index: i64,
pub input: String,
pub status: BatchTraderItemStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<Trader>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<ApiErrorBody>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum BatchTraderItemStatus {
/// `ok`
#[serde(rename = "ok")]
Ok,
/// `error`
#[serde(rename = "error")]
Error,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl BatchTraderItemStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Ok => "ok",
Self::Error => "error",
Self::Other(value) => value,
}
}
}
impl From<&str> for BatchTraderItemStatus {
fn from(value: &str) -> Self {
match value {
"ok" => Self::Ok,
"error" => Self::Error,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for BatchTraderItemStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for BatchTraderItemStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum BoardUpcomingStatus {
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// `not_configured`
#[serde(rename = "not_configured")]
NotConfigured,
/// `available`
#[serde(rename = "available")]
Available,
/// `capacity_limited`
#[serde(rename = "capacity_limited")]
CapacityLimited,
/// `source_unavailable`
#[serde(rename = "source_unavailable")]
SourceUnavailable,
/// `cold_unavailable`
#[serde(rename = "cold_unavailable")]
ColdUnavailable,
/// `deadline_unavailable`
#[serde(rename = "deadline_unavailable")]
DeadlineUnavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl BoardUpcomingStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Unknown => "unknown",
Self::NotConfigured => "not_configured",
Self::Available => "available",
Self::CapacityLimited => "capacity_limited",
Self::SourceUnavailable => "source_unavailable",
Self::ColdUnavailable => "cold_unavailable",
Self::DeadlineUnavailable => "deadline_unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for BoardUpcomingStatus {
fn from(value: &str) -> Self {
match value {
"unknown" => Self::Unknown,
"not_configured" => Self::NotConfigured,
"available" => Self::Available,
"capacity_limited" => Self::CapacityLimited,
"source_unavailable" => Self::SourceUnavailable,
"cold_unavailable" => Self::ColdUnavailable,
"deadline_unavailable" => Self::DeadlineUnavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for BoardUpcomingStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for BoardUpcomingStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum BoardUpcomingUnavailableScopes {
/// `category`
#[serde(rename = "category")]
Category,
/// `nfl`
#[serde(rename = "nfl")]
Nfl,
/// `cfb`
#[serde(rename = "cfb")]
Cfb,
/// `nba`
#[serde(rename = "nba")]
Nba,
/// `wnba`
#[serde(rename = "wnba")]
Wnba,
/// `euroleague-basketball`
#[serde(rename = "euroleague-basketball")]
EuroleagueBasketball,
/// `nhl`
#[serde(rename = "nhl")]
Nhl,
/// `mls`
#[serde(rename = "mls")]
Mls,
/// `valorant`
#[serde(rename = "valorant")]
Valorant,
/// `league-of-legends`
#[serde(rename = "league-of-legends")]
LeagueOfLegends,
/// `counter-strike-2`
#[serde(rename = "counter-strike-2")]
CounterStrike2,
/// `dota-2`
#[serde(rename = "dota-2")]
Dota2,
/// `registry`
#[serde(rename = "registry")]
Registry,
/// `union`
#[serde(rename = "union")]
Union,
/// `wave`
#[serde(rename = "wave")]
Wave,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl BoardUpcomingUnavailableScopes {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Category => "category",
Self::Nfl => "nfl",
Self::Cfb => "cfb",
Self::Nba => "nba",
Self::Wnba => "wnba",
Self::EuroleagueBasketball => "euroleague-basketball",
Self::Nhl => "nhl",
Self::Mls => "mls",
Self::Valorant => "valorant",
Self::LeagueOfLegends => "league-of-legends",
Self::CounterStrike2 => "counter-strike-2",
Self::Dota2 => "dota-2",
Self::Registry => "registry",
Self::Union => "union",
Self::Wave => "wave",
Self::Other(value) => value,
}
}
}
impl From<&str> for BoardUpcomingUnavailableScopes {
fn from(value: &str) -> Self {
match value {
"category" => Self::Category,
"nfl" => Self::Nfl,
"cfb" => Self::Cfb,
"nba" => Self::Nba,
"wnba" => Self::Wnba,
"euroleague-basketball" => Self::EuroleagueBasketball,
"nhl" => Self::Nhl,
"mls" => Self::Mls,
"valorant" => Self::Valorant,
"league-of-legends" => Self::LeagueOfLegends,
"counter-strike-2" => Self::CounterStrike2,
"dota-2" => Self::Dota2,
"registry" => Self::Registry,
"union" => Self::Union,
"wave" => Self::Wave,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for BoardUpcomingUnavailableScopes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for BoardUpcomingUnavailableScopes {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// One bucketed OHLC price candle. Prices are in provider [0, 1] units, truncated to 4 decimals at the rendering edge.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Candle {
/// Bucket-start unix epoch (seconds): UTC midnight for 1d, the ISO-week Monday's UTC midnight for 1w.
pub t: i64,
/// Open price in provider [0, 1] units (first observed point in the bucket).
pub o: f64,
/// High price in provider [0, 1] units (max observed in the bucket).
pub h: f64,
/// Low price in provider [0, 1] units (min observed in the bucket).
pub l: f64,
/// Close price in provider [0, 1] units (last observed point in the bucket).
pub c: f64,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Category {
/// `Basketball`
#[serde(rename = "Basketball")]
Basketball,
/// `Football`
#[serde(rename = "Football")]
Football,
/// `Baseball`
#[serde(rename = "Baseball")]
Baseball,
/// `Hockey`
#[serde(rename = "Hockey")]
Hockey,
/// `MMA`
#[serde(rename = "MMA")]
Mma,
/// `Boxing`
#[serde(rename = "Boxing")]
Boxing,
/// `Soccer`
#[serde(rename = "Soccer")]
Soccer,
/// `Cricket`
#[serde(rename = "Cricket")]
Cricket,
/// `Golf`
#[serde(rename = "Golf")]
Golf,
/// `Tennis`
#[serde(rename = "Tennis")]
Tennis,
/// `Esports`
#[serde(rename = "Esports")]
Esports,
/// `Racing`
#[serde(rename = "Racing")]
Racing,
/// `Table Tennis`
#[serde(rename = "Table Tennis")]
TableTennis,
/// `Pickleball`
#[serde(rename = "Pickleball")]
Pickleball,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Category {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Basketball => "Basketball",
Self::Football => "Football",
Self::Baseball => "Baseball",
Self::Hockey => "Hockey",
Self::Mma => "MMA",
Self::Boxing => "Boxing",
Self::Soccer => "Soccer",
Self::Cricket => "Cricket",
Self::Golf => "Golf",
Self::Tennis => "Tennis",
Self::Esports => "Esports",
Self::Racing => "Racing",
Self::TableTennis => "Table Tennis",
Self::Pickleball => "Pickleball",
Self::Other(value) => value,
}
}
}
impl From<&str> for Category {
fn from(value: &str) -> Self {
match value {
"Basketball" => Self::Basketball,
"Football" => Self::Football,
"Baseball" => Self::Baseball,
"Hockey" => Self::Hockey,
"MMA" => Self::Mma,
"Boxing" => Self::Boxing,
"Soccer" => Self::Soccer,
"Cricket" => Self::Cricket,
"Golf" => Self::Golf,
"Tennis" => Self::Tennis,
"Esports" => Self::Esports,
"Racing" => Self::Racing,
"Table Tennis" => Self::TableTennis,
"Pickleball" => Self::Pickleball,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Category {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Category {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Model-wide readiness, read in the same database snapshot as category_records. Individual category rows retain their own status.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CategorySkillModelReadiness {
pub status: Status,
pub model_version: String,
pub taxonomy_version: String,
pub observation_started_at: String,
pub as_of: String,
#[serde(default)]
pub source_last_success_at: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum CategorySkillSource {
/// `live`
#[serde(rename = "live")]
Live,
/// `partial`
#[serde(rename = "partial")]
Partial,
/// `degraded`
#[serde(rename = "degraded")]
Degraded,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl CategorySkillSource {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Live => "live",
Self::Partial => "partial",
Self::Degraded => "degraded",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for CategorySkillSource {
fn from(value: &str) -> Self {
match value {
"live" => Self::Live,
"partial" => Self::Partial,
"degraded" => Self::Degraded,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for CategorySkillSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for CategorySkillSource {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Forward-only category evidence from observed Polymarket taker fills. Status is category eligibility, not a global letter grade or a guarantee of positive edge. Scores are probability differences. Unknown and degraded rows withhold scores. Coverage is partial; observation counts are not lifetime market counts.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CategorySkillV2 {
pub status: Status,
pub model_version: String,
#[serde(default)]
pub taxonomy_version: Option<String>,
pub platform: String,
pub scope: String,
pub source_coverage: SourceCoverage,
pub canonical_category: String,
pub as_of: String,
#[serde(default)]
pub source_last_success_at: Option<String>,
pub observation_started_at: String,
#[serde(default)]
pub latest_observation_at: Option<String>,
pub independent_event_count: i64,
pub resolved_condition_count: i64,
pub unresolved_observation_count: i64,
#[serde(default)]
pub edge_mean: Option<f64>,
#[serde(default)]
pub edge_sd: Option<f64>,
#[serde(default)]
pub edge_se: Option<f64>,
#[serde(default)]
pub edge_lower_95: Option<f64>,
#[serde(default)]
pub brier_event_avg: Option<f64>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum CategoryWinRateStatus {
/// `measured`
#[serde(rename = "measured")]
Measured,
/// `not_enough_data`
#[serde(rename = "not_enough_data")]
NotEnoughData,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl CategoryWinRateStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Measured => "measured",
Self::NotEnoughData => "not_enough_data",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for CategoryWinRateStatus {
fn from(value: &str) -> Self {
match value {
"measured" => Self::Measured,
"not_enough_data" => Self::NotEnoughData,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for CategoryWinRateStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for CategoryWinRateStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Changed {
/// `scores`
#[serde(rename = "scores")]
Scores,
/// `status`
#[serde(rename = "status")]
Status,
/// `period`
#[serde(rename = "period")]
Period,
/// `live`
#[serde(rename = "live")]
Live,
/// `ended`
#[serde(rename = "ended")]
Ended,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Changed {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Scores => "scores",
Self::Status => "status",
Self::Period => "period",
Self::Live => "live",
Self::Ended => "ended",
Self::Other(value) => value,
}
}
}
impl From<&str> for Changed {
fn from(value: &str) -> Self {
match value {
"scores" => Self::Scores,
"status" => Self::Status,
"period" => Self::Period,
"live" => Self::Live,
"ended" => Self::Ended,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Changed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Changed {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ClvApplicability {
/// `applicable`
#[serde(rename = "applicable")]
Applicable,
/// `not_applicable`
#[serde(rename = "not_applicable")]
NotApplicable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ClvApplicability {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Applicable => "applicable",
Self::NotApplicable => "not_applicable",
Self::Other(value) => value,
}
}
}
impl From<&str> for ClvApplicability {
fn from(value: &str) -> Self {
match value {
"applicable" => Self::Applicable,
"not_applicable" => Self::NotApplicable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ClvApplicability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ClvApplicability {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ClvState {
/// `measured`
#[serde(rename = "measured")]
Measured,
/// `pending`
#[serde(rename = "pending")]
Pending,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// `not_applicable`
#[serde(rename = "not_applicable")]
NotApplicable,
/// `none`
#[serde(rename = "none")]
None,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ClvState {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Measured => "measured",
Self::Pending => "pending",
Self::Unavailable => "unavailable",
Self::NotApplicable => "not_applicable",
Self::None => "none",
Self::Other(value) => value,
}
}
}
impl From<&str> for ClvState {
fn from(value: &str) -> Self {
match value {
"measured" => Self::Measured,
"pending" => Self::Pending,
"unavailable" => Self::Unavailable,
"not_applicable" => Self::NotApplicable,
"none" => Self::None,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ClvState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ClvState {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Cohort {
/// `wider_holder`
#[serde(rename = "wider_holder")]
WiderHolder,
/// `in_play`
#[serde(rename = "in_play")]
InPlay,
/// `emerging_pile`
#[serde(rename = "emerging_pile")]
EmergingPile,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Cohort {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::WiderHolder => "wider_holder",
Self::InPlay => "in_play",
Self::EmergingPile => "emerging_pile",
Self::Other(value) => value,
}
}
}
impl From<&str> for Cohort {
fn from(value: &str) -> Self {
match value {
"wider_holder" => Self::WiderHolder,
"in_play" => Self::InPlay,
"emerging_pile" => Self::EmergingPile,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Cohort {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Cohort {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Combo {
/// Combo identifier: 0x followed by 62 lowercase hex characters. This differs from a binary market condition ID.
pub condition_id: String,
#[serde(default)]
pub leg_count: Option<i64>,
#[serde(default)]
pub prepared_at: Option<String>,
/// Whether the preparation event has been indexed.
pub legs_known: bool,
pub legs: Vec<ComboLeg>,
#[serde(default)]
pub window: Option<ComboWindowStats>,
#[serde(default)]
pub totals: Option<ComboTotals>,
/// Result of the YES conjunction, independent of the wallet side or its P&L.
pub status: ComboStatus,
/// Reason the conjunction result cannot be verified, when unknown.
#[serde(default)]
pub status_reason: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboCoverage {
pub state: ComboCoverageState,
pub ingest_enabled: bool,
pub cursors: Vec<ComboCoverageCursor>,
#[serde(default)]
pub max_lag_blocks: Option<i64>,
#[serde(default)]
pub oldest_cursor_update: Option<String>,
#[serde(default)]
pub lifecycle_lag_blocks: Option<i64>,
pub lifecycle_backfilling: bool,
/// Time the coverage state was evaluated; not a market-price timestamp.
pub computed_at: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboCoverageCursor {
/// Indexed contract.
pub contract: String,
pub last_block: i64,
pub seed_block: i64,
pub seed_origin: String,
#[serde(default)]
pub chain_finalized_block: Option<i64>,
#[serde(default)]
pub lag_blocks: Option<i64>,
pub updated_at: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ComboCoverageState {
/// `paused`
#[serde(rename = "paused")]
Paused,
/// `uninitialized`
#[serde(rename = "uninitialized")]
Uninitialized,
/// `stale`
#[serde(rename = "stale")]
Stale,
/// `backfilling`
#[serde(rename = "backfilling")]
Backfilling,
/// `live`
#[serde(rename = "live")]
Live,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ComboCoverageState {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Paused => "paused",
Self::Uninitialized => "uninitialized",
Self::Stale => "stale",
Self::Backfilling => "backfilling",
Self::Live => "live",
Self::Other(value) => value,
}
}
}
impl From<&str> for ComboCoverageState {
fn from(value: &str) -> Self {
match value {
"paused" => Self::Paused,
"uninitialized" => Self::Uninitialized,
"stale" => Self::Stale,
"backfilling" => Self::Backfilling,
"live" => Self::Live,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ComboCoverageState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ComboCoverageState {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// One combo with its newest 100 fills and all-time lifecycle totals. Paid API callers receive full access.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboDetail {
pub access: String,
pub combo: Combo,
pub fills: Vec<ComboFill>,
#[serde(default)]
pub lifecycle: Option<ComboLifecycleStats>,
pub coverage: ComboCoverage,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboFill {
/// Stable fill identity.
pub id: String,
pub block_number: i64,
pub log_index: i64,
pub block_timestamp: String,
pub tx_hash: String,
/// Taker wallet address.
#[serde(default)]
pub trader: Option<String>,
#[serde(default)]
pub trader_name: Option<String>,
#[serde(default)]
pub trader_grade: Option<Grade>,
/// Recorded wallet-wide trade time.
#[serde(default)]
pub last_traded_at: Option<String>,
pub action: Action,
/// Combo identifier: 0x followed by 62 lowercase hex characters. This differs from a binary market condition ID.
pub condition_id: String,
/// 0 is YES, 1 is NO.
pub outcome_index: i64,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub shares: String,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub collateral_usdc: String,
/// Execution price for the traded outcome; null for a zero-share event.
#[serde(default)]
pub price: Option<String>,
/// Execution price normalized to YES.
#[serde(default)]
pub price_yes: Option<String>,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub fee_usdc: String,
#[serde(default)]
pub leg_count: Option<i64>,
/// Result of the YES conjunction, independent of the wallet side or its P&L.
pub status: ComboStatus,
#[serde(default)]
pub status_reason: Option<String>,
pub won_legs: i64,
/// Shares per dollar at execution; not a forecast or guaranteed return.
#[serde(default)]
pub payout_multiplier: Option<String>,
pub legs: Vec<ComboLeg>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboFillsResponse {
pub object: String,
pub data: Vec<ComboFill>,
pub sort: ComboFillsResponseSort,
pub window: Window,
pub limit: i64,
pub coverage: ComboCoverage,
/// A continuation is available based on page fullness. Its next page may be empty.
pub has_more: bool,
/// Exclusive block_number:log_index continuation for recent ordering.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Continuation offset for largest or payout ordering.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_offset: Option<i64>,
pub meta: ResponseMeta,
/// Not computed for combo fill pages; omitted.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ComboFillsResponseSort {
/// `recent`
#[serde(rename = "recent")]
Recent,
/// `largest`
#[serde(rename = "largest")]
Largest,
/// `payout`
#[serde(rename = "payout")]
Payout,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ComboFillsResponseSort {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Recent => "recent",
Self::Largest => "largest",
Self::Payout => "payout",
Self::Other(value) => value,
}
}
}
impl From<&str> for ComboFillsResponseSort {
fn from(value: &str) -> Self {
match value {
"recent" => Self::Recent,
"largest" => Self::Largest,
"payout" => Self::Payout,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ComboFillsResponseSort {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ComboFillsResponseSort {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboLeg {
pub leg_index: i64,
pub outcome_index: i64,
pub module_id: i64,
pub state: ComboLegState,
#[serde(default)]
pub market: Option<ComboLegMarket>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboLegMarket {
/// Source of the leg facts.
pub source: ComboLegMarketSource,
/// Full binary CTF condition ID for this leg.
pub condition_id: String,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub slug: Option<String>,
#[serde(default)]
pub event_slug: Option<String>,
/// Provider-resolved URL including the referral tag.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub polymarket_url: Option<String>,
#[serde(default)]
pub outcome_label: Option<String>,
#[serde(default)]
pub image: Option<String>,
#[serde(default)]
pub category: Option<String>,
/// Provider outcome index; negative sentinels describe void or unresolved states.
#[serde(default)]
pub winning_outcome: Option<i64>,
#[serde(default)]
pub resolved_at: Option<String>,
/// A closed market may still have no terminal outcome.
#[serde(default)]
pub market_closed: Option<bool>,
/// Stored daily price for this leg outcome token; not a live quote.
#[serde(default)]
pub pick_price_daily: Option<f64>,
/// Observation time of the stored daily price.
#[serde(default)]
pub pick_price_as_of: Option<String>,
/// Whole days since the stored daily bucket.
#[serde(default)]
pub pick_price_age_days: Option<i64>,
/// Unmodified provider leg status when available.
#[serde(default)]
pub provider_leg_status: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ComboLegMarketSource {
/// `markets`
#[serde(rename = "markets")]
Markets,
/// `provider`
#[serde(rename = "provider")]
Provider,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ComboLegMarketSource {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Markets => "markets",
Self::Provider => "provider",
Self::Other(value) => value,
}
}
}
impl From<&str> for ComboLegMarketSource {
fn from(value: &str) -> Self {
match value {
"markets" => Self::Markets,
"provider" => Self::Provider,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ComboLegMarketSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ComboLegMarketSource {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ComboLegState {
/// `pending`
#[serde(rename = "pending")]
Pending,
/// `won`
#[serde(rename = "won")]
Won,
/// `lost`
#[serde(rename = "lost")]
Lost,
/// `void`
#[serde(rename = "void")]
Void,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ComboLegState {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Pending => "pending",
Self::Won => "won",
Self::Lost => "lost",
Self::Void => "void",
Self::Unknown => "unknown",
Self::Other(value) => value,
}
}
}
impl From<&str> for ComboLegState {
fn from(value: &str) -> Self {
match value {
"pending" => Self::Pending,
"won" => Self::Won,
"lost" => Self::Lost,
"void" => Self::Void,
"unknown" => Self::Unknown,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ComboLegState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ComboLegState {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboLifecycleStats {
pub splits: i64,
pub merges: i64,
pub redeems: i64,
pub compresses: i64,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub split_usdc: String,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub merge_usdc: String,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub redeemed_shares: String,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub redeemed_usdc: String,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub compressed_usdc: String,
pub last_event_at: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboLiveCount {
/// Traded combos whose YES conjunction remains pending.
pub count: i64,
/// Time the count was computed; check its age before using it.
pub computed_at: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboResponse {
pub object: String,
pub data: ComboDetail,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ComboStatus {
/// `pending`
#[serde(rename = "pending")]
Pending,
/// `won`
#[serde(rename = "won")]
Won,
/// `lost`
#[serde(rename = "lost")]
Lost,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ComboStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Pending => "pending",
Self::Won => "won",
Self::Lost => "lost",
Self::Unknown => "unknown",
Self::Other(value) => value,
}
}
}
impl From<&str> for ComboStatus {
fn from(value: &str) -> Self {
match value {
"pending" => Self::Pending,
"won" => Self::Won,
"lost" => Self::Lost,
"unknown" => Self::Unknown,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ComboStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ComboStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboTotals {
pub fills: i64,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub volume_usdc: String,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub largest_fill_usdc: String,
pub first_fill_at: String,
pub last_fill_at: String,
/// Last taker execution normalized to YES; not a current quote.
#[serde(default)]
pub last_fill_price_yes: Option<String>,
pub last_fill_outcome_index: i64,
pub last_fill_action: Action,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ComboWindowStats {
pub fills: i64,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub volume_usdc: String,
pub distinct_traders: i64,
/// Exact decimal string from the stored chain or SQL value; preserve precision until display.
pub largest_fill_usdc: String,
}
/// Stored live combo count. Null means the first count is unavailable, never zero.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CombosSummary {
#[serde(default)]
pub live: Option<ComboLiveCount>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CombosSummaryResponse {
pub object: String,
pub data: CombosSummary,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Competitors {
/// `provider_ids`
#[serde(rename = "provider_ids")]
ProviderIds,
/// `labels`
#[serde(rename = "labels")]
Labels,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Competitors {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::ProviderIds => "provider_ids",
Self::Labels => "labels",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for Competitors {
fn from(value: &str) -> Self {
match value {
"provider_ids" => Self::ProviderIds,
"labels" => Self::Labels,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Competitors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Competitors {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Consistency {
/// `live`
#[serde(rename = "live")]
Live,
/// `snapshot`
#[serde(rename = "snapshot")]
Snapshot,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Consistency {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Live => "live",
Self::Snapshot => "snapshot",
Self::Other(value) => value,
}
}
}
impl From<&str> for Consistency {
fn from(value: &str) -> Self {
match value {
"live" => Self::Live,
"snapshot" => Self::Snapshot,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Consistency {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Consistency {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ContentKind {
/// `learn`
#[serde(rename = "learn")]
Learn,
/// `glossary`
#[serde(rename = "glossary")]
Glossary,
/// `comparison`
#[serde(rename = "comparison")]
Comparison,
/// `research`
#[serde(rename = "research")]
Research,
/// `strategy`
#[serde(rename = "strategy")]
Strategy,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ContentKind {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Learn => "learn",
Self::Glossary => "glossary",
Self::Comparison => "comparison",
Self::Research => "research",
Self::Strategy => "strategy",
Self::Other(value) => value,
}
}
}
impl From<&str> for ContentKind {
fn from(value: &str) -> Self {
match value {
"learn" => Self::Learn,
"glossary" => Self::Glossary,
"comparison" => Self::Comparison,
"research" => Self::Research,
"strategy" => Self::Strategy,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ContentKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ContentKind {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ContentSearchResult {
/// Backend-owned stable editorial content identifier.
pub content_id: String,
/// Editorial content kind.
pub kind: ContentKind,
/// Backend-owned content slug.
pub slug: String,
/// Editorial title.
pub title: String,
/// Search-result excerpt.
pub excerpt: String,
/// Canonical first-party content URL.
pub url: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ContentType {
/// `application/json`
#[serde(rename = "application/json")]
ApplicationJson,
/// `application/x-ndjson`
#[serde(rename = "application/x-ndjson")]
ApplicationXNdjson,
/// `text/csv`
#[serde(rename = "text/csv")]
TextCsv,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ContentType {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::ApplicationJson => "application/json",
Self::ApplicationXNdjson => "application/x-ndjson",
Self::TextCsv => "text/csv",
Self::Other(value) => value,
}
}
}
impl From<&str> for ContentType {
fn from(value: &str) -> Self {
match value {
"application/json" => Self::ApplicationJson,
"application/x-ndjson" => Self::ApplicationXNdjson,
"text/csv" => Self::TextCsv,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ContentType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ContentType {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CounterpartyAnalysis {
pub status: CounterpartyAnalysisStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unavailable_reason: Option<String>,
/// Deterministic immutable membership snapshot for this detail response.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub snapshot_id: Option<String>,
/// Stable digest binding page cursors to one snapshot and execution set.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub analysis_id: Option<String>,
pub execution_count: i64,
pub available_execution_count: i64,
pub unavailable_execution_count: i64,
pub executions: Vec<CounterpartyExecution>,
/// Stable cursor after the last execution included in the bounded inline page.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub executions_next_cursor: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum CounterpartyAnalysisStatus {
/// `available`
#[serde(rename = "available")]
Available,
/// `partial`
#[serde(rename = "partial")]
Partial,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl CounterpartyAnalysisStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Available => "available",
Self::Partial => "partial",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for CounterpartyAnalysisStatus {
fn from(value: &str) -> Self {
match value {
"available" => Self::Available,
"partial" => Self::Partial,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for CounterpartyAnalysisStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for CounterpartyAnalysisStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CounterpartyExecution {
pub execution_id: String,
pub exchange_family: ExchangeFamily,
pub transaction_hash: String,
pub orders_matched_log_index: i64,
pub taker: CounterpartyParticipant,
pub makers: Vec<CounterpartyParticipant>,
/// Stable cursor after the last maker included in the bounded inline page.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub makers_next_cursor: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CounterpartyMakerPage {
pub analysis_id: String,
pub snapshot_id: String,
pub execution_id: String,
pub items: Vec<CounterpartyParticipant>,
/// Cursor bound to the analysis, execution, last full-denominator share total, and wallet.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CounterpartyMatchBreakdown {
pub match_type: MatchType,
pub maker_fill_count: i64,
/// Exact decimal shares.
pub filled_shares: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CounterpartyParticipant {
pub counterparty_key: String,
/// Lowercase Polygon wallet from the exact OrderFilled log.
pub execution_wallet: String,
pub identity_status: IdentityStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resolved_user_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub wallet_family: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resolution_block: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resolution_source: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grade: Option<String>,
/// Latest nonfuture recorded wallet-wide trade time for the tracked trader supplying this participant's grade. Omitted when unavailable; not the receipt time or the owner EOA's activity. Trader context is read with the response and is not frozen by execution membership snapshots.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_traded_at: Option<String>,
/// 0xinsider profile path segment for this participant, present only when the execution wallet has a tracked trader: `@<username>` when that username resolves to the trader alone, otherwise the trader's lowercase wallet. Percent-encode the part after `@` and append to `https://0xinsider.com/profile/`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profile_segment: Option<String>,
pub maker_fill_count: i64,
/// Exact decimal shares.
pub filled_shares: String,
/// Exact decimal USDC amount.
pub filled_usdc: String,
/// Participant share of the exact execution, from 0 through 100.
pub share_pct: String,
pub match_breakdown: Vec<CounterpartyMatchBreakdown>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Coverage {
/// `full_envelope_and_trades`
#[serde(rename = "full_envelope_and_trades")]
FullEnvelopeAndTrades,
/// `trades_only`
#[serde(rename = "trades_only")]
TradesOnly,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Coverage {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::FullEnvelopeAndTrades => "full_envelope_and_trades",
Self::TradesOnly => "trades_only",
Self::Other(value) => value,
}
}
}
impl From<&str> for Coverage {
fn from(value: &str) -> Self {
match value {
"full_envelope_and_trades" => Self::FullEnvelopeAndTrades,
"trades_only" => Self::TradesOnly,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Coverage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Coverage {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateMcpJsonRpcResponseBody {
/// JSON-RPC protocol version; this server accepts 2.0.
pub jsonrpc: String,
/// Request identifier echoed exactly in one JSON-RPC response; supported notifications omit it and receive an empty HTTP 202.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<serde_json::Value>,
/// MCP method to invoke.
pub method: McpMethod,
/// Method-specific MCP parameters.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub params: Option<serde_json::Map<String, serde_json::Value>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CreateMcpJsonRpcResponseResponse {
pub jsonrpc: String,
#[serde(default)]
pub id: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Map<String, serde_json::Value>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<CreateMcpJsonRpcResponseResponseError>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CreateMcpJsonRpcResponseResponseError {
pub code: i64,
pub message: String,
/// Optional JSON-serializable error data.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateWebhookRequest {
pub name: String,
/// Public HTTPS callback URL on the default port 443. Local, private, and internal targets are rejected, as is any explicit port other than 443 and any URL carrying credentials. Each user's URLs are unique after normalizing HTTPS scheme/host case, trailing DNS dots and port 443; path/query case is preserved. Pending verification and PATCH-disabled endpoints still reserve their stored URL. The destination must answer the signed webhook.verification challenge with a 2xx before POST /api/v1/webhooks/{id}/verify can activate the endpoint.
pub url: String,
pub event_types: Vec<WebhookEventType>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trade_filters: Option<LargeTradeSubscriptionFilters>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CreateWebhookResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CreateWebhookVerificationAttemptRequest {
/// The original one-time token returned by webhook creation or a URL change. Sent only in the request body and signed receiver challenge.
pub verification_token: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CreateWebhookVerificationAttemptResponse {
pub object: String,
pub data: WebhookVerificationAttempt,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum CredentialKind {
/// `api_key`
#[serde(rename = "api_key")]
ApiKey,
/// `oauth_grant`
#[serde(rename = "oauth_grant")]
OauthGrant,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl CredentialKind {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::ApiKey => "api_key",
Self::OauthGrant => "oauth_grant",
Self::Other(value) => value,
}
}
}
impl From<&str> for CredentialKind {
fn from(value: &str) -> Self {
match value {
"api_key" => Self::ApiKey,
"oauth_grant" => Self::OauthGrant,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for CredentialKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for CredentialKind {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Compact data age and coverage for a response body, always present on the operations that publish it. Read status and as_of to decide whether to use the body at all, and field_groups to see which part is weak. Everything here comes from stored observation clocks, so a cached body reports the same ages a freshly computed one does: meta.cached and meta.cache_age_s stay the only transport-time facts and neither makes this block newer. The per-field audit object is still available through expand=trust; this is the default summary of the same question.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DataQuality {
/// fresh when every group is fresh, unavailable when every group is unavailable, and partial in every other case.
pub status: DataQualityGroupStatus,
/// The oldest as_of among the groups that carry one: the age of the weakest clock this body rests on. Omitted when no group carries a clock.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub as_of: Option<String>,
/// One entry per field group. Entries may be added in later releases, so match on group rather than on position or length.
pub field_groups: Vec<DataQualityGroup>,
}
/// One group of response fields that share a writer and therefore share a clock.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DataQualityGroup {
/// Stable snake_case group name. Names are additive across releases, so match on the ones you know and ignore the rest.
pub group: String,
/// The table and column that write this group, named so the verdict can be audited (for example trader_rankings.computed_at).
pub owner: String,
/// fresh: served, and as_of carries this group's real observation or computation clock. partial: some of the group's fields are served and some are missing. unknown: served, and this read has no clock for it, so no age may be inferred. untracked: 0xinsider does not track this group for this subject, by design. unavailable: the group could not be served. New values may be added; treat one you do not recognize as unknown. fresh means the group is tracked and clocked, not that it is inside any particular tolerance: compare as_of against your own.
pub status: DataQualityGroupStatus,
/// When this group's values were observed or computed. Omitted whenever the read cannot measure it, and never filled with the serialization time, the cache time, or another group's clock.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub as_of: Option<String>,
/// Why the status is not fresh. Omitted when it is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum DataQualityGroupStatus {
/// `fresh`
#[serde(rename = "fresh")]
Fresh,
/// `partial`
#[serde(rename = "partial")]
Partial,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// `untracked`
#[serde(rename = "untracked")]
Untracked,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl DataQualityGroupStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Fresh => "fresh",
Self::Partial => "partial",
Self::Unknown => "unknown",
Self::Untracked => "untracked",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for DataQualityGroupStatus {
fn from(value: &str) -> Self {
match value {
"fresh" => Self::Fresh,
"partial" => Self::Partial,
"unknown" => Self::Unknown,
"untracked" => Self::Untracked,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for DataQualityGroupStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for DataQualityGroupStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DeleteWebhookResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Direction {
/// `YES`
#[serde(rename = "YES")]
Yes,
/// `NO`
#[serde(rename = "NO")]
No,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Direction {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Yes => "YES",
Self::No => "NO",
Self::Other(value) => value,
}
}
}
impl From<&str> for Direction {
fn from(value: &str) -> Self {
match value {
"YES" => Self::Yes,
"NO" => Self::No,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Direction {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum DirectionalSource {
/// `live`
#[serde(rename = "live")]
Live,
/// `degraded`
#[serde(rename = "degraded")]
Degraded,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl DirectionalSource {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Live => "live",
Self::Degraded => "degraded",
Self::Other(value) => value,
}
}
}
impl From<&str> for DirectionalSource {
fn from(value: &str) -> Self {
match value {
"live" => Self::Live,
"degraded" => Self::Degraded,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for DirectionalSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for DirectionalSource {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum DirectionalStatus {
/// `available`
#[serde(rename = "available")]
Available,
/// `unknown_ungrouped`
#[serde(rename = "unknown_ungrouped")]
UnknownUngrouped,
/// `unknown_stale`
#[serde(rename = "unknown_stale")]
UnknownStale,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl DirectionalStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Available => "available",
Self::UnknownUngrouped => "unknown_ungrouped",
Self::UnknownStale => "unknown_stale",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for DirectionalStatus {
fn from(value: &str) -> Self {
match value {
"available" => Self::Available,
"unknown_ungrouped" => Self::UnknownUngrouped,
"unknown_stale" => Self::UnknownStale,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for DirectionalStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for DirectionalStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// The full Max-only Edge Hunter board. Defaults cover all sports and esports with B grade floor, 48-hour horizon and at most 30 rows; common selections, legacy category, min_grade and limit narrow that projection. No pagination or ETag. Rows add frozen market_context discovery metadata.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterBoard {
/// When the ranked snapshot was computed. Date current quotes with their own ts values.
pub as_of: String,
pub horizon_hours: i64,
/// Requested holder-presence grade floor, S/A/B. A holder at that grade or better admits the row; other holders remain visible.
pub min_grade: MarketHolderGrade,
/// live uses the current graded holder pile; db_only is a weaker stored-data fallback.
pub ranking_source: RankingSource,
/// degraded means directional enrichment was unavailable.
pub directional_source: DirectionalSource,
#[serde(default)]
pub cached_age_seconds: Option<i64>,
pub viewer: String,
/// Always null: Edge Hunter has no free signal slot.
#[serde(default)]
pub free_rank: Option<i64>,
/// Ranked rows after normalized selections and grade filtering, before the view-specific limit cap.
pub total: i64,
pub rows: Vec<EdgeHunterRow>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterBoardResponse {
pub object: String,
pub data: EdgeHunterBoard,
pub meta: ResponseMeta,
/// Normalized axes requested with a selector or category; omitted on an unselected default read.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub selection: Option<EdgeHunterSelection>,
}
/// Category evidence over the full uncapped piled-side S/A/B holder allocation. It never changes signal membership, ordering, routing, or sizing.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterCategorySkill {
pub status: Status,
pub model_version: String,
#[serde(default)]
pub taxonomy_version: Option<String>,
pub platform: String,
pub scope: String,
pub source_coverage: SourceCoverage,
pub observation_started_at: String,
pub as_of: String,
#[serde(default)]
pub canonical_category: Option<String>,
pub eligible_holders: i64,
pub covered_holders: i64,
#[serde(default)]
pub backed_sharp_usd: Option<f64>,
#[serde(default)]
pub covered_backed_usd: Option<f64>,
#[serde(default)]
pub coverage_pct: Option<f64>,
#[serde(default)]
pub weighted_edge_mean: Option<f64>,
#[serde(default)]
pub weighted_holder_lower_mean: Option<f64>,
#[serde(default)]
pub specialist_backed_usd: Option<f64>,
#[serde(default)]
pub specialist_backed_usd_pct: Option<f64>,
#[serde(default)]
pub largest_holder_backed_usd_pct: Option<f64>,
#[serde(default)]
pub minimum_holder_event_count: Option<i64>,
}
/// Per-sport accountable funnel for the full observation snapshot, returned on every page.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterFunnelReport {
pub sports: Vec<EdgeHunterSportFunnelReport>,
}
/// Metadata frozen with the read generation. Values are nullable when the provider/canonical source has no value; no title-based inference or current-clock fallback.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterMarketContext {
/// Normalized machine/provider league identity. Known wire/tag aliases normalize; other provider identities match exact lowercase keys.
#[serde(default)]
pub league: Option<String>,
/// Exact provider series slug, usable in series_slugs selection.
#[serde(default)]
pub series_slug: Option<String>,
/// Exact provider sports_market_type, usable in market_types selection.
#[serde(default)]
pub market_type: Option<String>,
/// Exact local canonical metadata writer market_canonical.updated_at. This is not a provider observation clock; null means unavailable.
#[serde(default)]
pub metadata_updated_at: Option<String>,
}
/// One explicitly observation-only holder-pile measurement. It is evidence for cohort evaluation, not an execution instruction, and is isolated from the funded Edge Hunter signals view.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterObservation {
/// The side profitable wallets hold, as a provider-backed display label. Canonical spelling of piled_side (#16310), same value: when provider group context is unavailable it may remain a bare Yes/No/Over/Under, so do not use it alone as participant identity.
#[serde(default)]
pub side: Option<String>,
/// Recorded side backing score; higher values indicate stronger backing.
pub backing_score: f64,
/// Signed share of graded money on the side, in [-1, 1]. Canonical spelling of smart_score (#16309, #16310), same value.
pub side_share: f64,
/// Raw Polymarket condition id.
pub condition_id: String,
/// Provider-backed Polymarket CLOB token id for the piled outcome. Rows without a verified token terminate before emission.
pub token_id: String,
/// Canonical sport bucket.
pub category: Category,
/// Raw provider category as stored.
#[serde(default)]
pub raw_category: Option<String>,
/// Provider-backed market title.
pub title: String,
#[serde(default)]
pub event_slug: Option<String>,
/// Provider event id when available.
#[serde(default)]
pub event_id: Option<String>,
/// Provider parent-event id used as the first event-cap identity when available.
#[serde(default)]
pub parent_event_id: Option<String>,
/// Provider-backed kickoff time in UTC.
pub game_start_time: String,
/// UTC instant when this row finished provider/holder evaluation.
pub observed_at: String,
/// Source cohort or additive projection view. emerging_pile is projected from wider_holder after source computation, overlaps its funnel denominator, and uses the source row's directional evidence.
pub cohort: Cohort,
/// Which evaluation place in its sport the row took this refresh. most_held: a wider_holder place for up to 8 markets per sport kicking off within 24 hours, most graded holders first, kept until kickoff while the holder page still shows a card or could not be read; also every in_play row, whose places go most graded holders first. soonest_kickoff: a wider_holder place among the sport's 8 soonest kickoffs, which is not kept. emerging_pile rows carry their source wider_holder row's value. Filter on most_held to keep the most-held selection. most_held_48h: every row of an admission=most_held_48h response, which is that opt-in selection (up to 8 markets per sport within 48 hours, most graded holders first, recomputed every refresh), including a market a default place also evaluates; it never appears on a default response.
pub admitted_by: AdmittedBy,
/// Always true. This row must not be routed to an order executor.
pub observation_only: bool,
/// Nullable provider-backed piled-outcome display label. When provider group context is unavailable, it may remain a bare Yes/No/Over/Under; do not use it alone as participant identity. Deprecated (#16310): `side` is the canonical spelling and carries the same value; this key stays on the wire.
#[serde(default)]
pub piled_side: Option<String>,
/// Provider binary-column selector: 0 selects outcome_yes/token_id_yes; 1 selects outcome_no/token_id_no. It does not identify home/away or a participant. Use piled_side together with title/event context for display.
pub piled_outcome_index: i64,
/// Aggregate piled-side market midpoint recorded at observed_at. This is an observation price mark, not a wallet entry cost.
pub backed_price: f64,
/// Piled-side graded-holder dollar concentration.
pub sharp_pct: f64,
/// Raw graded-holder USD on the piled outcome.
pub backed_sharp_usd: f64,
pub s_count: i64,
pub a_count: i64,
pub b_count: i64,
/// Piled-side S/A/B holder count.
pub graded_holders: i64,
pub top_grade: MarketHolderGrade,
/// Canonical signed holder-pile score. Deprecated (#16310): `side_share` is the canonical spelling and carries the same value; this key stays on the wire.
pub smart_score: f64,
/// Cumulative provider taker volume in shares, not cash or dollars.
pub volume: f64,
/// Recorded conviction score; higher values indicate stronger conviction.
pub conviction_score: f64,
/// Whether the provider holder page came from the shared cache or a live provider read.
pub provider_read_source: ProviderReadSource,
/// True only when the provider page read for each outcome token listed every holder of that token. Observation collection reads one 100-row page per token; a token with more holders stays incomplete. False means holder counts and dollar totals measure the observed positive-holder subset. sharp_pct describes that observed subset and is not a lower bound on the full-population percentage. Incomplete scans cannot satisfy a future capital-promotion gate.
pub holder_scan_complete: bool,
/// Proven provider holder observation time. A warm cache hit uses only the original provider completion time from its companion metadata, never cache-read time. A wider_holder or emerging_pile row whose holder read did not finish in the current pass may reuse that market's last pregame holder page for up to 15 minutes; an in_play row may reuse its last in-play page, read after the provider reported the game live and never before that market's own kickoff, for up to 10 minutes. Both ages are counted when the snapshot is computed. Either reuse carries that page's original time here and provider_read_source cached. A pregame market more than an hour before kickoff reuses its last page the same way until that page is 10 minutes old, and only then is read again. Once that market's own kickoff has passed, an in_play row with provider_read_source cached, a warm hit or a reuse, never carries a page read before it. Null, malformed, future, or stale holder time fails in-play closed.
#[serde(default)]
pub holder_snapshot_at: Option<String>,
/// Truthful state of the directional read, which classifies each graded holder by its fresh synced legs across the game's markets and, when holder_scan_complete is true, by Polymarket's currentValue on both outcomes of this market. A wider_holder row can remain emitted with unavailable and terminal wider_holder_emitted; in_play fails closed instead and terminates as in_play_directional_unavailable.
pub directional_status: DirectionalStatus,
#[serde(default)]
pub one_way_holder_count: Option<i64>,
#[serde(default)]
pub hedged_holder_count: Option<i64>,
#[serde(default)]
pub one_way_graded_usd: Option<f64>,
#[serde(default)]
pub directional_confidence: Option<f64>,
/// Recorded side ordering score; higher values sort first.
pub directional_rank_score: f64,
/// 1-based rank within this observation cohort and snapshot.
pub rank: i64,
/// Required nullable frozen metadata context. Null only during the legacy-generation transition; selected reads require sidecar readiness and fail closed with 503 instead.
#[serde(default)]
pub market_context: Option<EdgeHunterMarketContext>,
}
/// Measured coverage of the frozen canonical observation generation and this requested page. For sports and esports reads, the first four counts remain global through category, cohort and all selectors; with admission=most_held_48h they count that selection's generation, where every wider_holder candidate it did not take is capacity_limited. They are neither a census of all provider markets nor a count of successfully evaluated holders. Selected and returned counts describe the requested projection. An unmappable legacy category synthesizes a zero-work empty generation with all six counts zero and no degradation reasons; it does not read the all-sports cache.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterObservationCoverage {
/// Sum of canonical source-generation funnel input across all sports, before category, cohort or selector projection.
pub input_markets: i64,
/// Canonical source-generation input assigned capacity_limited. These markets were intentionally outside the bounded holder-work admission and are not missing emitted rows.
pub capacity_limited_markets: i64,
/// All wider_holder and in_play observations emitted in the canonical source generation, before cohort, category or selector projection. Emerging pile is an overlapping projection and does not add source emissions.
pub emitted_observations: i64,
/// Canonical source-generation markets whose attempted holder scan did not establish complete coverage, including incomplete scans that did not emit an observation. Collection reads one 100-row page per token, so a market with more holders on a token counts here by design; this count does not by itself degrade the generation.
pub incomplete_holder_markets: i64,
/// Observations matching the requested cohort, category, selectors and applicable pinned projection cutoff before pagination.
pub selected_observations: i64,
/// Observation rows on this page; equals data.length.
pub returned_observations: i64,
}
/// One observed cause of operational degradation in the canonical all-sports observation generation. Reasons are independent of the one-terminal-per-input funnel and remain unchanged through category, cohort and selector projection. Intentional capacity limits and an unconfigured upcoming source are not degradation reasons.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterObservationDegradationReason {
/// Recorded producer cause. holder_scan_incomplete is retired and no longer emitted: the one-page holder read is an intentional bound, disclosed by each row's holder_scan_complete and by coverage.incomplete_holder_markets, and it does not degrade a generation. directional_unavailable can accompany an emitted wider_holder row; live_source_stale means provider live evidence exceeded its freshness bound. Other values identify reconciliation, applicable board-source or terminal failure evidence.
pub reason: EdgeHunterObservationDegradationReasonReason,
/// Canonical sport for the recorded cause; null only for generation-wide evidence without a sport attribution.
#[serde(default)]
pub sport: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum EdgeHunterObservationDegradationReasonReason {
/// `holder_scan_incomplete`
#[serde(rename = "holder_scan_incomplete")]
HolderScanIncomplete,
/// `live_source_stale`
#[serde(rename = "live_source_stale")]
LiveSourceStale,
/// `directional_unavailable`
#[serde(rename = "directional_unavailable")]
DirectionalUnavailable,
/// `funnel_unreconciled`
#[serde(rename = "funnel_unreconciled")]
FunnelUnreconciled,
/// `board_live_unavailable`
#[serde(rename = "board_live_unavailable")]
BoardLiveUnavailable,
/// `board_upcoming_unavailable`
#[serde(rename = "board_upcoming_unavailable")]
BoardUpcomingUnavailable,
/// `provider_unavailable`
#[serde(rename = "provider_unavailable")]
ProviderUnavailable,
/// `holder_deadline_unavailable`
#[serde(rename = "holder_deadline_unavailable")]
HolderDeadlineUnavailable,
/// `holder_computation_unavailable`
#[serde(rename = "holder_computation_unavailable")]
HolderComputationUnavailable,
/// `missing_stored_market`
#[serde(rename = "missing_stored_market")]
MissingStoredMarket,
/// `price_unavailable`
#[serde(rename = "price_unavailable")]
PriceUnavailable,
/// `in_play_stale_observed`
#[serde(rename = "in_play_stale_observed")]
InPlayStaleObserved,
/// `in_play_directional_unavailable`
#[serde(rename = "in_play_directional_unavailable")]
InPlayDirectionalUnavailable,
/// `internal_unclassified`
#[serde(rename = "internal_unclassified")]
InternalUnclassified,
/// `board_source_unavailable`
#[serde(rename = "board_source_unavailable")]
BoardSourceUnavailable,
/// `board_deadline_unavailable`
#[serde(rename = "board_deadline_unavailable")]
BoardDeadlineUnavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl EdgeHunterObservationDegradationReasonReason {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::HolderScanIncomplete => "holder_scan_incomplete",
Self::LiveSourceStale => "live_source_stale",
Self::DirectionalUnavailable => "directional_unavailable",
Self::FunnelUnreconciled => "funnel_unreconciled",
Self::BoardLiveUnavailable => "board_live_unavailable",
Self::BoardUpcomingUnavailable => "board_upcoming_unavailable",
Self::ProviderUnavailable => "provider_unavailable",
Self::HolderDeadlineUnavailable => "holder_deadline_unavailable",
Self::HolderComputationUnavailable => "holder_computation_unavailable",
Self::MissingStoredMarket => "missing_stored_market",
Self::PriceUnavailable => "price_unavailable",
Self::InPlayStaleObserved => "in_play_stale_observed",
Self::InPlayDirectionalUnavailable => "in_play_directional_unavailable",
Self::InternalUnclassified => "internal_unclassified",
Self::BoardSourceUnavailable => "board_source_unavailable",
Self::BoardDeadlineUnavailable => "board_deadline_unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for EdgeHunterObservationDegradationReasonReason {
fn from(value: &str) -> Self {
match value {
"holder_scan_incomplete" => Self::HolderScanIncomplete,
"live_source_stale" => Self::LiveSourceStale,
"directional_unavailable" => Self::DirectionalUnavailable,
"funnel_unreconciled" => Self::FunnelUnreconciled,
"board_live_unavailable" => Self::BoardLiveUnavailable,
"board_upcoming_unavailable" => Self::BoardUpcomingUnavailable,
"provider_unavailable" => Self::ProviderUnavailable,
"holder_deadline_unavailable" => Self::HolderDeadlineUnavailable,
"holder_computation_unavailable" => Self::HolderComputationUnavailable,
"missing_stored_market" => Self::MissingStoredMarket,
"price_unavailable" => Self::PriceUnavailable,
"in_play_stale_observed" => Self::InPlayStaleObserved,
"in_play_directional_unavailable" => Self::InPlayDirectionalUnavailable,
"internal_unclassified" => Self::InternalUnclassified,
"board_source_unavailable" => Self::BoardSourceUnavailable,
"board_deadline_unavailable" => Self::BoardDeadlineUnavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for EdgeHunterObservationDegradationReasonReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for EdgeHunterObservationDegradationReasonReason {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A market whose wider_holder card left this selection before kickoff, with why it left.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterObservationDeparture {
/// Raw Polymarket condition id.
pub condition_id: String,
#[serde(default)]
pub event_slug: Option<String>,
/// Canonical sport bucket the departed card carried.
pub category: Category,
/// The departed card's admitted_by: most_held_48h on an admission=most_held_48h response.
pub admitted_by: AdmittedBy,
/// Kickoff the departed card carried, in UTC. The entry leaves the list once it passes.
pub game_start_time: String,
/// The departed card's observed_at: when its last evaluation finished.
pub last_observed_at: String,
/// snapshot_as_of of the first snapshot of this selection without the card.
pub departed_at: String,
/// Why the market has no card: its terminal in this selection's funnel in the snapshot it left, and while that terminal reports a read that did not finish (holder_deadline_unavailable, provider_unavailable, holder_computation_unavailable, internal_unclassified), the terminal of each later snapshot until one answers; an answered reason never changes. primary_slate_candidate: the market joined the funded slate, which view=signals covers. capacity_limited: it lost its evaluation place without another outcome. missing_stored_market: the market was no longer in the stored market universe. Any other value means what it means in the funnel, for example split_holder_pile, no_current_graded_holder or resolved.
pub reason: EdgeHunterObservationTerminalReason,
}
/// Closed 25-value terminal-reason vocabulary for the accountable Edge Hunter observation funnel. capacity_limited is intentional bounded provider-work admission and does not itself set the snapshot degraded. board_source_unavailable is a completed board-source failure; board_deadline_unavailable means live-board work missed either an internal configured-scope deadline or the outer fair-wave deadline; both classify only already-started rows, so for the upcoming source read funnel.sports[].board_upcoming_status instead; provider_unavailable is reserved for an attempted holder-provider failure; holder_deadline_unavailable is holder cache/provider absolute-deadline exhaustion.
///
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum EdgeHunterObservationTerminalReason {
/// `outside_horizon`
#[serde(rename = "outside_horizon")]
OutsideHorizon,
/// `resolved`
#[serde(rename = "resolved")]
Resolved,
/// `provider_closed`
#[serde(rename = "provider_closed")]
ProviderClosed,
/// `provider_excluded`
#[serde(rename = "provider_excluded")]
ProviderExcluded,
/// `invalid_market`
#[serde(rename = "invalid_market")]
InvalidMarket,
/// `missing_token`
#[serde(rename = "missing_token")]
MissingToken,
/// `missing_stored_market`
#[serde(rename = "missing_stored_market")]
MissingStoredMarket,
/// `not_provider_live`
#[serde(rename = "not_provider_live")]
NotProviderLive,
/// `board_source_unavailable`
#[serde(rename = "board_source_unavailable")]
BoardSourceUnavailable,
/// `board_deadline_unavailable`
#[serde(rename = "board_deadline_unavailable")]
BoardDeadlineUnavailable,
/// `primary_slate_candidate`
#[serde(rename = "primary_slate_candidate")]
PrimarySlateCandidate,
/// `zero_indexed_holder_research`
#[serde(rename = "zero_indexed_holder_research")]
ZeroIndexedHolderResearch,
/// `capacity_limited`
#[serde(rename = "capacity_limited")]
CapacityLimited,
/// `provider_unavailable`
#[serde(rename = "provider_unavailable")]
ProviderUnavailable,
/// `holder_deadline_unavailable`
#[serde(rename = "holder_deadline_unavailable")]
HolderDeadlineUnavailable,
/// `holder_computation_unavailable`
#[serde(rename = "holder_computation_unavailable")]
HolderComputationUnavailable,
/// `holder_scan_incomplete`
#[serde(rename = "holder_scan_incomplete")]
HolderScanIncomplete,
/// `no_current_graded_holder`
#[serde(rename = "no_current_graded_holder")]
NoCurrentGradedHolder,
/// `split_holder_pile`
#[serde(rename = "split_holder_pile")]
SplitHolderPile,
/// `price_unavailable`
#[serde(rename = "price_unavailable")]
PriceUnavailable,
/// `wider_holder_emitted`
#[serde(rename = "wider_holder_emitted")]
WiderHolderEmitted,
/// `in_play_emitted`
#[serde(rename = "in_play_emitted")]
InPlayEmitted,
/// `in_play_stale_observed`
#[serde(rename = "in_play_stale_observed")]
InPlayStaleObserved,
/// `in_play_directional_unavailable`
#[serde(rename = "in_play_directional_unavailable")]
InPlayDirectionalUnavailable,
/// `internal_unclassified`
#[serde(rename = "internal_unclassified")]
InternalUnclassified,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl EdgeHunterObservationTerminalReason {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::OutsideHorizon => "outside_horizon",
Self::Resolved => "resolved",
Self::ProviderClosed => "provider_closed",
Self::ProviderExcluded => "provider_excluded",
Self::InvalidMarket => "invalid_market",
Self::MissingToken => "missing_token",
Self::MissingStoredMarket => "missing_stored_market",
Self::NotProviderLive => "not_provider_live",
Self::BoardSourceUnavailable => "board_source_unavailable",
Self::BoardDeadlineUnavailable => "board_deadline_unavailable",
Self::PrimarySlateCandidate => "primary_slate_candidate",
Self::ZeroIndexedHolderResearch => "zero_indexed_holder_research",
Self::CapacityLimited => "capacity_limited",
Self::ProviderUnavailable => "provider_unavailable",
Self::HolderDeadlineUnavailable => "holder_deadline_unavailable",
Self::HolderComputationUnavailable => "holder_computation_unavailable",
Self::HolderScanIncomplete => "holder_scan_incomplete",
Self::NoCurrentGradedHolder => "no_current_graded_holder",
Self::SplitHolderPile => "split_holder_pile",
Self::PriceUnavailable => "price_unavailable",
Self::WiderHolderEmitted => "wider_holder_emitted",
Self::InPlayEmitted => "in_play_emitted",
Self::InPlayStaleObserved => "in_play_stale_observed",
Self::InPlayDirectionalUnavailable => "in_play_directional_unavailable",
Self::InternalUnclassified => "internal_unclassified",
Self::Other(value) => value,
}
}
}
impl From<&str> for EdgeHunterObservationTerminalReason {
fn from(value: &str) -> Self {
match value {
"outside_horizon" => Self::OutsideHorizon,
"resolved" => Self::Resolved,
"provider_closed" => Self::ProviderClosed,
"provider_excluded" => Self::ProviderExcluded,
"invalid_market" => Self::InvalidMarket,
"missing_token" => Self::MissingToken,
"missing_stored_market" => Self::MissingStoredMarket,
"not_provider_live" => Self::NotProviderLive,
"board_source_unavailable" => Self::BoardSourceUnavailable,
"board_deadline_unavailable" => Self::BoardDeadlineUnavailable,
"primary_slate_candidate" => Self::PrimarySlateCandidate,
"zero_indexed_holder_research" => Self::ZeroIndexedHolderResearch,
"capacity_limited" => Self::CapacityLimited,
"provider_unavailable" => Self::ProviderUnavailable,
"holder_deadline_unavailable" => Self::HolderDeadlineUnavailable,
"holder_computation_unavailable" => Self::HolderComputationUnavailable,
"holder_scan_incomplete" => Self::HolderScanIncomplete,
"no_current_graded_holder" => Self::NoCurrentGradedHolder,
"split_holder_pile" => Self::SplitHolderPile,
"price_unavailable" => Self::PriceUnavailable,
"wider_holder_emitted" => Self::WiderHolderEmitted,
"in_play_emitted" => Self::InPlayEmitted,
"in_play_stale_observed" => Self::InPlayStaleObserved,
"in_play_directional_unavailable" => Self::InPlayDirectionalUnavailable,
"internal_unclassified" => Self::InternalUnclassified,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for EdgeHunterObservationTerminalReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for EdgeHunterObservationTerminalReason {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Full Edge Hunter observations list envelope. Required degradation_reasons explains the source-generation verdict; required coverage distinguishes source input, intentional capacity exclusions, source emissions, incomplete holder scans, selection and the returned page. The view literal distinguishes advanced collections, including empty pages.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterObservationsResponse {
pub object: String,
pub view: String,
pub data: Vec<EdgeHunterObservation>,
pub has_more: bool,
/// Pass as cursor for the next page. Always sent; null on the last page.
#[serde(default)]
pub next_cursor: Option<String>,
/// Completion time of the shared observation snapshot pinned by the cursor.
pub snapshot_as_of: String,
/// True when an operational failure or unknown provider-board, holder, directional, reconciliation, or internal completeness state made this snapshot partial. This is snapshot-wide and can retain degradation that the funnel's one-terminal-per-input accounting cannot represent. Inspect degradation_reasons for every recorded source-generation cause and its nullable canonical sport attribution. Intentional bounds do not by themselves set this field: capacity_limited rows remain fully accounted in the funnel, and a holder scan the one-page read leaves incomplete counts in coverage.incomplete_holder_markets; board_upcoming_status=not_configured is not a failure.
pub degraded: bool,
/// Sorted, deduplicated causes from the canonical all-sports generation, unchanged by category, cohort or selectors; with admission=most_held_48h, from that selection's generation. Empty exactly when degraded is false. Capacity-limited input, the one-page holder bound (holder_scan_complete=false) and board_upcoming_status=not_configured do not by themselves add a reason.
pub degradation_reasons: Vec<EdgeHunterObservationDegradationReason>,
pub coverage: EdgeHunterObservationCoverage,
pub funnel: EdgeHunterFunnelReport,
pub meta: ResponseMeta,
/// Normalized axes requested with a selector or category; omitted on an unselected default read.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub selection: Option<EdgeHunterSelection>,
/// Echoes the opt-in admission rule a request named; omitted on a default read. When present, data, funnel, coverage, degraded and degradation_reasons describe that selection, not the default one.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub admission: Option<String>,
/// cohort=wider_holder only, for the default selection and for admission=most_held_48h: markets that had a wider_holder card in an earlier snapshot of the same selection, have none in this one, and have not kicked off. Newest departed_at first. An entry stays until the game_start_time it carries passes, and leaves as soon as the market has a card again; while it stays only a reason that reports an unfinished read changes (see reason), and a later departure of the same market is a new entry. Filtered by the same category and selectors as data, and identical on every page of one snapshot. At most 336 entries; when more are open, the oldest departures leave first. An empty array means no card of this selection is departed. Omitted on in_play and emerging_pile, and on a snapshot that could not establish its departures, never reported as an empty array. Tracking that restarts after its record is lost starts empty.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub departures: Option<Vec<EdgeHunterObservationDeparture>>,
}
/// One provider outcome in fixed YES/NO order, including its frozen token identity and independently observed quote. Null current_price means no verified current quote is available.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterOutcome {
pub index: i64,
#[serde(default)]
pub label: Option<String>,
#[serde(default)]
pub provider_id: Option<i64>,
/// Frozen provider CLOB token ID for this exact outcome in YES/NO order. Null when unavailable; not a participant identity.
#[serde(default)]
pub token_id: Option<String>,
#[serde(default)]
pub current_price: Option<EdgeHunterPrice>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterParticipant {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_id: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logo: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logo_mark_dark: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub color: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub headshot: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub headshot_revision: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tour: Option<String>,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterPrice {
/// Provider probability as a decimal string. Keep its precision until display.
pub price: String,
pub basis: Basis,
/// Source observation clock in epoch milliseconds, represented as a string.
pub ts: String,
/// Backend validation clock in epoch milliseconds. Omitted when no current-source validation is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub validated_at: Option<String>,
}
/// One ranked pre-game sports market where graded sharp money is piled on one side, with required-status category evidence from partial forward-observed Polymarket fills.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterRankedSignal {
/// The side profitable wallets hold, as a provider-backed display label. Canonical spelling of piled_side (#16310), same value: when provider group context is unavailable it may remain a bare Yes/No/Over/Under, so do not use it alone as participant identity.
#[serde(default)]
pub side: Option<String>,
/// UTC time at which the snapshot that ranked this row was computed. Canonical spelling of signal_created_at (#16310), same value.
pub ranked_at: String,
/// Recorded side backing score; higher values indicate stronger backing.
pub backing_score: f64,
/// Signed share of graded money on the side, (yes_usd - no_usd)/(yes_usd + no_usd) in [-1, 1] (side-yes positive, side-no negative). Canonical spelling of smart_score (#16309, #16310), same value.
#[serde(default)]
pub side_share: Option<f64>,
/// Polymarket condition id.
pub condition_id: String,
/// UTC time at which the immutable signal snapshot was computed. Every row from one snapshot shares this value; it is not provider market creation time and is not rewritten at request time. Deprecated (#16310): `ranked_at` is the canonical spelling and carries the same value; this key stays on the wire.
pub signal_created_at: String,
/// Polymarket CLOB token id (ERC1155 asset id, decimal string) for the PILED outcome; null when unavailable.
#[serde(default)]
pub token_id: Option<String>,
/// Canonical sport bucket (e.g. Basketball, Tennis); null when the raw category has no canonical mapping.
#[serde(default)]
pub category: Option<Category>,
/// Raw provider category as stored (e.g. NBA, EPL).
#[serde(default)]
pub raw_category: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub event_slug: Option<String>,
/// Kickoff (UTC). In the future at SNAPSHOT time and within the requested horizon; because the response is served from a shared snapshot cached up to the ~180s TTL, a served kickoff can be up to ~180s in the past relative to the response time. Not a live guarantee that the game has not yet started.
#[serde(default)]
pub game_start_time: Option<String>,
/// Nullable provider-backed piled-outcome display label. When provider group context is unavailable, it may remain a bare Yes/No/Over/Under; do not use it alone as participant identity. Deprecated (#16310): `side` is the canonical spelling and carries the same value; this key stays on the wire.
#[serde(default)]
pub piled_side: Option<String>,
/// Provider binary-column selector: 0 selects outcome_yes/token_id_yes; 1 selects outcome_no/token_id_no. It does not identify home/away or a participant. Use piled_side together with title/event context for display.
pub piled_outcome_index: i64,
/// Piled-side dollar concentration backed_usd / (yes_usd + no_usd), in (0.5, 1] for a real pile; null when there is no sharp USD.
#[serde(default)]
pub sharp_pct: Option<f64>,
/// Raw piled-side sharp-money USD.
pub backed_sharp_usd: f64,
/// S-grade graded holders on the piled side.
pub s_count: i64,
/// A-grade graded holders on the piled side.
pub a_count: i64,
/// B-grade graded holders on the piled side.
pub b_count: i64,
/// Piled-side graded holder count (s_count + a_count + b_count).
pub graded_holders: i64,
/// Best grade present on the piled side; null when none.
#[serde(default)]
pub top_grade: Option<MarketHolderGrade>,
/// Canonical sharp-money score (yes_usd - no_usd)/(yes_usd + no_usd) in [-1, 1] (piled-yes positive, piled-no negative). Deprecated (#16310): `side_share` is the canonical spelling and carries the same value; this key stays on the wire.
#[serde(default)]
pub smart_score: Option<f64>,
/// Cumulative provider taker volume in shares, not cash or dollars.
#[serde(default)]
pub volume: Option<f64>,
/// Aggregate recent flow direction on the market; null when unavailable.
#[serde(default)]
pub net_side: Option<LargeTradeInsertedV2DataSide>,
/// Recorded conviction score; higher values indicate stronger conviction.
pub conviction_score: f64,
/// Piled-side graded holders read one-way: their fresh open legs across the signal game's markets (cross-market within the one game; moneyline+spread family only) all back the same team, or, when the market's holder scan was complete, Polymarket's currentValue shows no opposite leg on this market worth 10% of the backed leg and no fresh leg opposes it. Null when the directional read was not computed (no groupable game, no holder-level data on this ranking path, or the enrichment read failed) or classified nobody.
#[serde(default)]
pub one_way_holder_count: Option<i64>,
/// Piled-side graded holders classified HEDGED across the game by fresh legs (they back two or more distinct teams). A wallet long both outcomes of this market is not one-way and not counted here. Null when the directional read was not computed or classified nobody.
#[serde(default)]
pub hedged_holder_count: Option<i64>,
/// Piled-side graded USD held by one-way wallets (share-weighted allocation of backed_sharp_usd). Null when the directional read was not computed or classified nobody.
#[serde(default)]
pub one_way_graded_usd: Option<f64>,
/// One-way fraction of the piled graded dollars, in [0, 1] -- the metric orthogonal to sharp_pct. Stale, unknown, hedged, and two-sided dollars dilute it toward zero (conservative). Null when the directional read was not computed or classified nobody.
#[serde(default)]
pub directional_confidence: Option<f64>,
/// Recorded side ordering score; higher values sort first.
pub directional_rank_score: f64,
pub category_skill: EdgeHunterCategorySkill,
/// 1-based rank within the (min_grade-filtered) ranked result.
pub rank: i64,
/// Required nullable frozen metadata context. Null only during the legacy-generation transition; selected reads require sidecar readiness and fail closed with 503 instead.
#[serde(default)]
pub market_context: Option<EdgeHunterMarketContext>,
}
/// One full Max row from Edge Hunter. signal is populated on every row this endpoint returns. Identity and quotes preserve provider values; unknown values stay null.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterRow {
pub rank: i64,
pub state: String,
pub free: bool,
pub condition_id: String,
#[serde(default)]
pub event_slug: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub category: Option<Category>,
#[serde(default)]
pub raw_category: Option<String>,
#[serde(default)]
pub game_start_time: Option<String>,
/// Cumulative provider taker volume in shares, not cash in USD. Null when unavailable.
#[serde(default)]
pub volume: Option<f64>,
pub participants: Vec<EdgeHunterParticipant>,
#[serde(default)]
pub image: Option<String>,
pub outcomes: Vec<EdgeHunterOutcome>,
#[serde(default)]
pub signal: Option<EdgeHunterSignal>,
/// Required nullable frozen metadata context. Null only during the legacy-generation transition; selected reads require sidecar readiness and fail closed with 503 instead.
#[serde(default)]
pub market_context: Option<EdgeHunterMarketContext>,
}
/// Normalized requested selection axes. Only requested axes appear; omitted for unselected defaults. Arrays are nonempty, sorted and deduplicated. OR within an axis, AND across axes.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterSelection {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kind: Option<Kind>,
/// Resolved legacy canonical bucket or non_sport for an unmappable category.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
/// Canonical sport buckets, including Esports and Table Tennis, matched case-insensitively. Unknown names and league aliases such as NFL are rejected in sports; use category for legacy bucket aliases or leagues for exact league selection. Omission covers every sport admitted by the selected view.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sports: Option<Vec<String>>,
/// Normalized machine/provider league identities, such as nfl, cfb, nba, epl, cs2, lol, dota2 or valorant. Known wire/tag aliases normalize; other provider keys match exact lowercase identity. This is separate from category: category=NFL selects Football, while leagues=nfl selects NFL only. Read market_context.league for authoritative values.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub leagues: Option<Vec<String>>,
/// Exact provider sports_market_type values. This is an open provider taxonomy, distinct from exact market IDs. Read market_context.market_type; missing selected metadata excludes the row.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub market_types: Option<Vec<String>>,
/// Exact market selections. Raw provider condition IDs and the existing mkt_-prefixed aliases normalize to raw IDs. Unknown well-formed IDs return an empty result.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition_ids: Option<Vec<String>>,
/// Exact provider event slugs. Use event_slug returned on the row; no title or slug-prefix guessing.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_slugs: Option<Vec<String>>,
/// Exact provider series slugs. Read market_context.series_slug; a missing selected series excludes the row.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub series_slugs: Option<Vec<String>>,
}
/// Backed-side analytics, token identity, frozen ranked_at and aggregate category evidence from the ranked snapshot, plus a separately dated current quote. Scores rank attention; they are not probabilities or order instructions.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterSignal {
#[serde(default)]
pub piled_side: Option<String>,
pub piled_outcome_index: i64,
/// Fraction of graded money on the backed side, not a percentage from 0 to 100.
#[serde(default)]
pub sharp_pct: Option<f64>,
pub backed_sharp_usd: f64,
pub s_count: i64,
pub a_count: i64,
pub b_count: i64,
pub graded_holders: i64,
#[serde(default)]
pub top_grade: Option<MarketHolderGrade>,
#[serde(default)]
pub smart_score: Option<f64>,
#[serde(default)]
pub net_side: Option<String>,
pub conviction_score: f64,
#[serde(default)]
pub one_way_holder_count: Option<i64>,
#[serde(default)]
pub hedged_holder_count: Option<i64>,
#[serde(default)]
pub one_way_graded_usd: Option<f64>,
#[serde(default)]
pub directional_confidence: Option<f64>,
pub directional_rank_score: f64,
#[serde(default)]
pub current_price: Option<EdgeHunterPrice>,
/// Frozen provider CLOB token ID for the backed outcome. Null when unavailable; matches outcomes[piled_outcome_index].token_id when the shared provider context is available.
#[serde(default)]
pub token_id: Option<String>,
/// UTC computation time copied from this ranked row's frozen signal_created_at. It dates the ranking, not the separately observed quote, metadata writer, or wallet entry.
pub ranked_at: String,
/// Existing aggregate category evidence copied from the same ranked snapshot. Read its status, coverage and own clocks; it never changes admission or ordering and is not a predicted return.
pub category_skill: EdgeHunterCategorySkill,
}
/// Full Edge Hunter signals list envelope. The view literal distinguishes advanced collections, including empty pages.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterSignalsResponse {
pub object: String,
pub view: String,
pub data: Vec<EdgeHunterRankedSignal>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
/// Normalized axes requested with a selector or category; omitted on an unselected default read.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub selection: Option<EdgeHunterSelection>,
}
/// Independent sports-board supply plus stored-universe terminal accounting for one canonical sport.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EdgeHunterSportFunnelReport {
pub sport: Category,
/// Unique condition ids independently visible on the provider-first sports board.
pub board_input: i64,
/// Whether the always-applicable live-board source completed as available. False can mean a completed source failure (board_source_unavailable) or live-board work missing an internal configured-scope deadline or the outer fair-wave deadline (board_deadline_unavailable); inspect terminals to distinguish them.
pub board_live_available: bool,
/// Whether a provider-backed upcoming-board source is configured and applicable for this sport. False means not applicable, not provider failure.
pub board_upcoming_configured: bool,
/// Whether every configured upcoming-board scope completed as available. False with board_upcoming_configured=false means not applicable. When board_upcoming_configured is true and this flag is false, read board_upcoming_status for the cause (it reads unknown, i.e. no recorded cause, only on a snapshot cached before that field existed, which self-clears within one TTL): the board_source_unavailable and board_deadline_unavailable terminals are assigned only to already-started rows (the live half) and are structurally 0 for the upcoming source, so they never explain this flag.
pub board_upcoming_available: bool,
/// Why the upcoming-board source is (un)available. Board supply is one canonical-sport union: the bare category owns live truth and every configured composed league scope contributes upcoming rows; folded leagues without a configured board (currently NCAAB and CFL) are not in the upcoming union. available: every configured scope completed truthfully (a successful empty schedule still counts). capacity_limited: provider pagination or a configured upcoming cache published an intentionally bounded complete-event prefix; those rows are excluded from diagnostic-universe input and must not be treated as a complete upcoming universe. Cache-bound prefixes are limited to an exact 5,000,000-byte final envelope. source_unavailable: composition failed before a truthful union; resolved scope readers turn half failures into cold_unavailable, so fresh producers are whole-union identity reconciliation or a registry contract failure and carry zero rows. cold_unavailable: every scope completed but at least one reported its upcoming half unavailable because no servable entry was inside the stale-serve bound and the background warm did not land in time; it is not by itself proof of a provider outage. deadline_unavailable: an internal configured-scope deadline or the outer bounded fair wave expired. An internal deadline may retain healthy bare-category or sibling-scope rows; the outer wave records zero rows. These upcoming fields do not describe league live-membership availability. If the bare-category live scope fails, all live rows are dropped even when a league scope completed, because the bare category is the sole live-truth owner. not_configured: no upcoming scope applies to the sport. unknown: exactly one cause -- a snapshot cached before this field existed whose legacy flags recorded an unavailable-but-configured half without saying why. Every freshly computed snapshot reports a concrete status, and a legacy available or not-configured row is reconstructed exactly, so unknown self-clears within one TTL. board_upcoming_available is exactly board_upcoming_status == available.
pub board_upcoming_status: BoardUpcomingStatus,
/// Configured upcoming scopes that did not complete as available. Values are category, a provider league tag slug (nfl, cfb, nba, wnba, euroleague-basketball, nhl, mls, valorant, league-of-legends, counter-strike-2, or dota-2), registry when the compiled scope/projection contract drifted, union when cross-scope identity reconciliation failed, or wave when the outer fair-wave deadline expired before scope-level evidence returned. Empty means no unavailable upcoming scope was identified; this includes healthy/not-configured rows and a legacy cached row. Observation league scopes are upcoming-only and perform no live-membership read.
pub board_upcoming_unavailable_scopes: Vec<BoardUpcomingUnavailableScopes>,
/// Stored-universe rows plus provider-board rows missing from storage.
pub input: i64,
/// Sparse counts over the closed 25-value terminal vocabulary: outside_horizon, resolved, provider_closed, provider_excluded, invalid_market, missing_token, missing_stored_market, not_provider_live, board_source_unavailable, board_deadline_unavailable, primary_slate_candidate, zero_indexed_holder_research, capacity_limited, provider_unavailable, holder_deadline_unavailable, holder_computation_unavailable, holder_scan_incomplete, no_current_graded_holder, split_holder_pile, price_unavailable, wider_holder_emitted, in_play_emitted, in_play_stale_observed, in_play_directional_unavailable, or internal_unclassified. primary_slate_candidate means exact admission by the funded route's raw shared signals query before provider/holder enrichment; recent-flow rows rejected by its event, bucket, or total caps remain eligible for wider_holder measurement. capacity_limited is intentional bounded provider-work admission, is fully accounted here, and does not itself set degraded=true. board_source_unavailable means a completed board source was unavailable; board_deadline_unavailable means live-board work missed either an internal configured-scope deadline or the outer fair-wave deadline; provider_unavailable means an attempted holder-provider read failed; holder_deadline_unavailable means holder cache/provider work missed the absolute request deadline; holder_computation_unavailable means post-holder provider or DB-backed price/metadata evaluation was unavailable.
pub terminals: std::collections::BTreeMap<String, i64>,
/// Sum of every sparse terminal count.
pub terminal_total: i64,
/// True exactly when input equals terminal_total.
pub reconciled: bool,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum EnrichmentStatus {
/// `available`
#[serde(rename = "available")]
Available,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl EnrichmentStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Available => "available",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for EnrichmentStatus {
fn from(value: &str) -> Self {
match value {
"available" => Self::Available,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for EnrichmentStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for EnrichmentStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ErrorCode {
/// `bad_request`
#[serde(rename = "bad_request")]
BadRequest,
/// `invalid_api_key`
#[serde(rename = "invalid_api_key")]
InvalidApiKey,
/// `subscription_required`
#[serde(rename = "subscription_required")]
SubscriptionRequired,
/// `forbidden`
#[serde(rename = "forbidden")]
Forbidden,
/// `insufficient_scope`
#[serde(rename = "insufficient_scope")]
InsufficientScope,
/// `not_found`
#[serde(rename = "not_found")]
NotFound,
/// `account_locked`
#[serde(rename = "account_locked")]
AccountLocked,
/// `rate_limited`
#[serde(rename = "rate_limited")]
RateLimited,
/// `rate_limit_unavailable`
#[serde(rename = "rate_limit_unavailable")]
RateLimitUnavailable,
/// `internal_error`
#[serde(rename = "internal_error")]
InternalError,
/// `request_timeout`
#[serde(rename = "request_timeout")]
RequestTimeout,
/// `endpoint_retired`
#[serde(rename = "endpoint_retired")]
EndpointRetired,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ErrorCode {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::BadRequest => "bad_request",
Self::InvalidApiKey => "invalid_api_key",
Self::SubscriptionRequired => "subscription_required",
Self::Forbidden => "forbidden",
Self::InsufficientScope => "insufficient_scope",
Self::NotFound => "not_found",
Self::AccountLocked => "account_locked",
Self::RateLimited => "rate_limited",
Self::RateLimitUnavailable => "rate_limit_unavailable",
Self::InternalError => "internal_error",
Self::RequestTimeout => "request_timeout",
Self::EndpointRetired => "endpoint_retired",
Self::Other(value) => value,
}
}
}
impl From<&str> for ErrorCode {
fn from(value: &str) -> Self {
match value {
"bad_request" => Self::BadRequest,
"invalid_api_key" => Self::InvalidApiKey,
"subscription_required" => Self::SubscriptionRequired,
"forbidden" => Self::Forbidden,
"insufficient_scope" => Self::InsufficientScope,
"not_found" => Self::NotFound,
"account_locked" => Self::AccountLocked,
"rate_limited" => Self::RateLimited,
"rate_limit_unavailable" => Self::RateLimitUnavailable,
"internal_error" => Self::InternalError,
"request_timeout" => Self::RequestTimeout,
"endpoint_retired" => Self::EndpointRetired,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ErrorCode {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ErrorReason {
/// `cursor_expired`
#[serde(rename = "cursor_expired")]
CursorExpired,
/// `unknown_endpoint`
#[serde(rename = "unknown_endpoint")]
UnknownEndpoint,
/// `pick_not_released`
#[serde(rename = "pick_not_released")]
PickNotReleased,
/// `trader_not_tracked`
#[serde(rename = "trader_not_tracked")]
TraderNotTracked,
/// `market_out_of_scope`
#[serde(rename = "market_out_of_scope")]
MarketOutOfScope,
/// `read_model_warming`
#[serde(rename = "read_model_warming")]
ReadModelWarming,
/// `database_unavailable`
#[serde(rename = "database_unavailable")]
DatabaseUnavailable,
/// `request_accounting_unavailable`
#[serde(rename = "request_accounting_unavailable")]
RequestAccountingUnavailable,
/// `idempotency_in_progress`
#[serde(rename = "idempotency_in_progress")]
IdempotencyInProgress,
/// `webhook_delivery_in_progress`
#[serde(rename = "webhook_delivery_in_progress")]
WebhookDeliveryInProgress,
/// `webhook_secret_rotation_not_prepared`
#[serde(rename = "webhook_secret_rotation_not_prepared")]
WebhookSecretRotationNotPrepared,
/// `webhook_secret_rotation_overlap_active`
#[serde(rename = "webhook_secret_rotation_overlap_active")]
WebhookSecretRotationOverlapActive,
/// `sandbox_api_key`
#[serde(rename = "sandbox_api_key")]
SandboxApiKey,
/// `api_key_in_query`
#[serde(rename = "api_key_in_query")]
ApiKeyInQuery,
/// `subscription_inactive`
#[serde(rename = "subscription_inactive")]
SubscriptionInactive,
/// `monthly_quota_exceeded`
#[serde(rename = "monthly_quota_exceeded")]
MonthlyQuotaExceeded,
/// `invalid_query`
#[serde(rename = "invalid_query")]
InvalidQuery,
/// `unknown_query_parameter`
#[serde(rename = "unknown_query_parameter")]
UnknownQueryParameter,
/// `invalid_path`
#[serde(rename = "invalid_path")]
InvalidPath,
/// `invalid_body`
#[serde(rename = "invalid_body")]
InvalidBody,
/// `unsupported_media_type`
#[serde(rename = "unsupported_media_type")]
UnsupportedMediaType,
/// `payload_too_large`
#[serde(rename = "payload_too_large")]
PayloadTooLarge,
/// `method_not_allowed`
#[serde(rename = "method_not_allowed")]
MethodNotAllowed,
/// `ip_rate_limited`
#[serde(rename = "ip_rate_limited")]
IpRateLimited,
/// `ip_throttled`
#[serde(rename = "ip_throttled")]
IpThrottled,
/// `export_expired`
#[serde(rename = "export_expired")]
ExportExpired,
/// `freshness_ceiling_unsatisfied`
#[serde(rename = "freshness_ceiling_unsatisfied")]
FreshnessCeilingUnsatisfied,
/// `max_plan_required`
#[serde(rename = "max_plan_required")]
MaxPlanRequired,
/// `endpoint_retired`
#[serde(rename = "endpoint_retired")]
EndpointRetired,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ErrorReason {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::CursorExpired => "cursor_expired",
Self::UnknownEndpoint => "unknown_endpoint",
Self::PickNotReleased => "pick_not_released",
Self::TraderNotTracked => "trader_not_tracked",
Self::MarketOutOfScope => "market_out_of_scope",
Self::ReadModelWarming => "read_model_warming",
Self::DatabaseUnavailable => "database_unavailable",
Self::RequestAccountingUnavailable => "request_accounting_unavailable",
Self::IdempotencyInProgress => "idempotency_in_progress",
Self::WebhookDeliveryInProgress => "webhook_delivery_in_progress",
Self::WebhookSecretRotationNotPrepared => "webhook_secret_rotation_not_prepared",
Self::WebhookSecretRotationOverlapActive => "webhook_secret_rotation_overlap_active",
Self::SandboxApiKey => "sandbox_api_key",
Self::ApiKeyInQuery => "api_key_in_query",
Self::SubscriptionInactive => "subscription_inactive",
Self::MonthlyQuotaExceeded => "monthly_quota_exceeded",
Self::InvalidQuery => "invalid_query",
Self::UnknownQueryParameter => "unknown_query_parameter",
Self::InvalidPath => "invalid_path",
Self::InvalidBody => "invalid_body",
Self::UnsupportedMediaType => "unsupported_media_type",
Self::PayloadTooLarge => "payload_too_large",
Self::MethodNotAllowed => "method_not_allowed",
Self::IpRateLimited => "ip_rate_limited",
Self::IpThrottled => "ip_throttled",
Self::ExportExpired => "export_expired",
Self::FreshnessCeilingUnsatisfied => "freshness_ceiling_unsatisfied",
Self::MaxPlanRequired => "max_plan_required",
Self::EndpointRetired => "endpoint_retired",
Self::Other(value) => value,
}
}
}
impl From<&str> for ErrorReason {
fn from(value: &str) -> Self {
match value {
"cursor_expired" => Self::CursorExpired,
"unknown_endpoint" => Self::UnknownEndpoint,
"pick_not_released" => Self::PickNotReleased,
"trader_not_tracked" => Self::TraderNotTracked,
"market_out_of_scope" => Self::MarketOutOfScope,
"read_model_warming" => Self::ReadModelWarming,
"database_unavailable" => Self::DatabaseUnavailable,
"request_accounting_unavailable" => Self::RequestAccountingUnavailable,
"idempotency_in_progress" => Self::IdempotencyInProgress,
"webhook_delivery_in_progress" => Self::WebhookDeliveryInProgress,
"webhook_secret_rotation_not_prepared" => Self::WebhookSecretRotationNotPrepared,
"webhook_secret_rotation_overlap_active" => Self::WebhookSecretRotationOverlapActive,
"sandbox_api_key" => Self::SandboxApiKey,
"api_key_in_query" => Self::ApiKeyInQuery,
"subscription_inactive" => Self::SubscriptionInactive,
"monthly_quota_exceeded" => Self::MonthlyQuotaExceeded,
"invalid_query" => Self::InvalidQuery,
"unknown_query_parameter" => Self::UnknownQueryParameter,
"invalid_path" => Self::InvalidPath,
"invalid_body" => Self::InvalidBody,
"unsupported_media_type" => Self::UnsupportedMediaType,
"payload_too_large" => Self::PayloadTooLarge,
"method_not_allowed" => Self::MethodNotAllowed,
"ip_rate_limited" => Self::IpRateLimited,
"ip_throttled" => Self::IpThrottled,
"export_expired" => Self::ExportExpired,
"freshness_ceiling_unsatisfied" => Self::FreshnessCeilingUnsatisfied,
"max_plan_required" => Self::MaxPlanRequired,
"endpoint_retired" => Self::EndpointRetired,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ErrorReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ErrorReason {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplayEvent {
/// Opaque event identity: the ef_-encoded whale_alerts.id, stable across cursor formats. Deduplicate on this, never on cursor or sequence.
pub id: String,
pub r#type: String,
/// Cursor positioned at this event: its (inserted_xid, id) commit-order position. Store the page's next_cursor to continue; this one resumes from exactly this event.
pub cursor: String,
/// whale_alerts.id of the event. Not monotonic across a replay: events arrive in commit order, so a lower id can follow a higher one when its write finished later. Order and resume by cursor, deduplicate by id.
pub sequence: i64,
pub published_at: String,
/// What the event announces. Every field is present on every event; a field added later is additive, so a client tolerates keys it does not know.
pub payload: EventReplayEventPayload,
/// Present only with expand=trade: the base trade fields for this row, read at request time from one query per page. GET /api/v1/large-trades/{id} adds counterparty_analysis; replay does not include it. traded_at, side, size_usd, price, outcome, token_id, recorded_signal_score and trader.grade_at_trade with its status are the row's event-time facts; trader.grade, trader.username, signal_score, suspicion_score, suspicion_track and market title/slug/category are enrichment that can move after the event. null when that route would answer 404 for the row (its trader or market is not synced yet).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trade: Option<LargeTrade>,
pub source: EventReplaySource,
pub freshness: EventReplayFreshness,
}
/// What the event announces. Every field is present on every event; a field added later is additive, so a client tolerates keys it does not know.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplayEventPayload {
/// Whale trades this event announces. One event is minted per stored whale trade, so this is 1.
pub count: i64,
/// The raw whale_alerts.id of the trade, the same number as sequence. GET /api/v1/whale-trades/{id} accepts it and returns the trade with its wallet, grade, size, price and market.
pub whale_alert_id: i64,
/// The Polymarket condition id of the market the trade was in.
pub condition_id: String,
/// 0xinsider's numeric id for the wallet's trader row. It is not the trd_ id, which is derived from the address; read the address and grade from the whale trade.
pub trader_id: i64,
/// Provider discriminator. Polymarket only.
pub platform: String,
/// Keys this release does not name. The API adds fields additively; they are kept here.
#[serde(flatten)]
pub extra: serde_json::Map<String, serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplayFreshness {
pub status: String,
pub observed_at: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplayMeta {
/// Unique request ID (req_ prefix). The same value as the X-Request-Id response header, the request's usage accounting row and its log lines.
pub request_id: String,
pub cached: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_age_s: Option<i64>,
/// Advisory request weight (relative compute cost). 1 for simple reads; higher for heavier endpoints. Not a credit/price.
pub cost: i64,
pub replay: EventReplayMetaReplay,
pub retention: EventReplayMetaRetention,
pub completeness: EventReplayMetaCompleteness,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplayMetaCompleteness {
/// complete: the page carries every durable event matching the filters after the cursor below the commit horizon, up to limit. caught_up: nothing matching after the cursor is visible below the horizon; read pending_beyond_horizon to tell an idle stream from held rows, and note next_cursor has still advanced to the horizon.
pub status: EventReplayMetaCompletenessStatus,
pub reason: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum EventReplayMetaCompletenessStatus {
/// `complete`
#[serde(rename = "complete")]
Complete,
/// `caught_up`
#[serde(rename = "caught_up")]
CaughtUp,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl EventReplayMetaCompletenessStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Complete => "complete",
Self::CaughtUp => "caught_up",
Self::Other(value) => value,
}
}
}
impl From<&str> for EventReplayMetaCompletenessStatus {
fn from(value: &str) -> Self {
match value {
"complete" => Self::Complete,
"caught_up" => Self::CaughtUp,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for EventReplayMetaCompletenessStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for EventReplayMetaCompletenessStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplayMetaReplay {
/// The request cursor in canonical form (an id-only cursor is re-encoded), or the zero position when omitted.
pub from_cursor: String,
/// Identical to next_cursor. After a full page, the cursor of the last event; after a page that is not full, the commit horizon itself, since every row below it, matching or not, has been examined. An empty page therefore still advances.
pub to_cursor: String,
/// whale_alerts.id component of the request cursor (0 when omitted).
pub from_sequence: i64,
/// whale_alerts.id of the last event on this page, or from_sequence when the page is empty. Can sit below the position next_cursor encodes after a page that is not full.
pub to_sequence: i64,
/// Events are ordered by the position at which their write became visible (whale_alerts.inserted_xid), then by whale_alerts.id, and a page is bounded by the oldest write transaction still open when it was read. Before 2026-09-22 this read whale_alerts_id_asc; that order could skip a late-committing lower id (#16180).
pub ordering: String,
/// True when committed whale-trade rows newer than this page's commit-visibility horizon exist. They are held until every older write transaction has finished and are served on a later request, so a caught_up page with this true is not the end of the stream: poll again. A long open write transaction on the database (a single-transaction backfill) is what keeps this true for more than a few seconds.
pub pending_beyond_horizon: bool,
/// The effective filter set this page ran with and next_cursor is bound to. Absent on an unfiltered walk.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filters: Option<EventReplayMetaReplayFilters>,
/// The expansions applied to every event on this page; empty when none.
pub expand: Vec<String>,
}
/// The effective filter set this page ran with and next_cursor is bound to. Absent on an unfiltered walk.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplayMetaReplayFilters {
/// The trader parameter as given.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trader: Option<String>,
/// The raw provider condition id after mkt_ is stripped.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min_grade: Option<Grade>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min_size: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplayMetaRetention {
pub status: String,
pub retained_events: i64,
pub cursor_expired: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EventReplaySource {
pub kind: String,
pub producer_family: String,
pub owner: String,
pub provider_fetch_at_request_time: bool,
}
/// A lossless decimal atom rendered from the canonical NUMERIC or provider value. The value is a decimal string and must be parsed with a decimal library; it is never a display string and must not be converted through a binary float. `scale` is the source decimal scale. The field is omitted when its source is unavailable.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExactDecimal {
/// Plain decimal text at full source precision, including a minus sign for negative values and trailing zeros when the source scale carries them. Parse as an arbitrary-precision decimal.
pub value: String,
/// Unit of the value, such as `USD`, `USD/share`, or `shares`.
pub unit: String,
/// Number of digits after the decimal point in `value`'s source atom.
pub scale: i64,
/// Backend-owned source or derivation basis. Treat it as provenance, not as a display label.
pub basis: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ExchangeFamily {
/// `ctf_v2`
#[serde(rename = "ctf_v2")]
CtfV2,
/// `neg_risk_ctf_v2`
#[serde(rename = "neg_risk_ctf_v2")]
NegRiskCtfV2,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ExchangeFamily {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::CtfV2 => "ctf_v2",
Self::NegRiskCtfV2 => "neg_risk_ctf_v2",
Self::Other(value) => value,
}
}
}
impl From<&str> for ExchangeFamily {
fn from(value: &str) -> Self {
match value {
"ctf_v2" => Self::CtfV2,
"neg_risk_ctf_v2" => Self::NegRiskCtfV2,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ExchangeFamily {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ExchangeFamily {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Expand {
/// `strategy`
#[serde(rename = "strategy")]
Strategy,
/// `categories`
#[serde(rename = "categories")]
Categories,
/// `quant_metrics`
#[serde(rename = "quant_metrics")]
QuantMetrics,
/// `trust`
#[serde(rename = "trust")]
Trust,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Expand {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Strategy => "strategy",
Self::Categories => "categories",
Self::QuantMetrics => "quant_metrics",
Self::Trust => "trust",
Self::Other(value) => value,
}
}
}
impl From<&str> for Expand {
fn from(value: &str) -> Self {
match value {
"strategy" => Self::Strategy,
"categories" => Self::Categories,
"quant_metrics" => Self::QuantMetrics,
"trust" => Self::Trust,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Expand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Expand {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Discriminated by `type`. A `type` this release does not know is kept whole in
/// `Unknown`, so a new kind never fails a response.
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum ExploreEntry {
/// `type` is `group`.
Group(ExploreGroup),
/// `type` is `standalone`.
Standalone(ExploreStandalone),
/// A `type` this release does not know, kept as sent.
Unknown(serde_json::Value),
}
impl<'de> Deserialize<'de> for ExploreEntry {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = serde_json::Value::deserialize(deserializer)?;
match value.get("type").and_then(serde_json::Value::as_str) {
Some("group") => serde_json::from_value(value)
.map(Self::Group)
.map_err(de::Error::custom),
Some("standalone") => serde_json::from_value(value)
.map(Self::Standalone)
.map_err(de::Error::custom),
_ => Ok(Self::Unknown(value)),
}
}
}
impl Serialize for ExploreEntry {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Group(inner) => inner.serialize(serializer),
Self::Standalone(inner) => inner.serialize(serializer),
Self::Unknown(inner) => inner.serialize(serializer),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExploreFacetValue {
pub value: String,
pub label: String,
pub count: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExploreFacets {
pub categories: Vec<ExploreFacetValue>,
pub platforms: Vec<ExploreFacetValue>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExploreGroup {
pub r#type: String,
pub event_slug: String,
pub parent_title: String,
#[serde(default)]
pub image: Option<String>,
/// Provider platform. Always polymarket, or null when the row carries no stored value.
#[serde(default)]
pub platform: Option<String>,
#[serde(default)]
pub category: Option<String>,
/// Markets in the event cluster, ranked by volume with condition_id as the tie-breaker. The selected representative is retained within the 12-market cap.
pub markets: Vec<ExploreMarket>,
/// The volume of the group's highest-volume market, which is markets[0]: the same figure as that market's volume, Polymarket's cumulative taker volume in shares rather than cash. Null when unavailable.
#[serde(default)]
pub rep_volume: Option<f64>,
/// Canonical key since #16304; rep_whales is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub rep_large_trades: Option<i64>,
#[serde(default)]
pub rep_whales: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExploreMarket {
pub id: String,
pub condition_id: String,
/// Non-empty market title.
pub title: String,
/// Provider-native market slug.
#[serde(default)]
pub slug: Option<String>,
/// First-party market page slug used for internal links.
#[serde(default)]
pub url_slug: Option<String>,
#[serde(default)]
pub image: Option<String>,
#[serde(default)]
pub icon: Option<String>,
#[serde(default)]
pub category: Option<String>,
/// Provider platform. Always polymarket, or null when the row carries no stored value.
#[serde(default)]
pub platform: Option<String>,
/// closed once Polymarket has closed trading or the market has resolved; active otherwise. The same rule labels a market on markets/search, markets/explore and market/{condition_id}/snapshot.
pub status: MarketSearchResultStatus,
/// Polymarket's traded-volume figure for the market, as last stored: cumulative taker volume in shares, each counted at its $1 settlement value, not the cash that changed hands (100 shares bought at $0.40 add 100, not 40). It can lag Polymarket's current figure. sort=volume orders by it. Null when unavailable.
#[serde(default)]
pub volume: Option<f64>,
#[serde(default)]
pub liquidity: Option<f64>,
/// Canonical key since #16304 (Polymarket's noun is large trade); whale_trade_count is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub large_trade_count: Option<i64>,
#[serde(default)]
pub whale_trade_count: Option<i64>,
/// Canonical key since #16304; whale_distinct_wallets is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub large_trade_distinct_wallets: Option<i64>,
#[serde(default)]
pub whale_distinct_wallets: Option<i64>,
/// Canonical key since #16304; whale_total_usd is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub large_trade_total_usd: Option<f64>,
#[serde(default)]
pub whale_total_usd: Option<f64>,
/// Canonical key since #16304; whale_last_trade_at is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub large_trade_last_at: Option<String>,
#[serde(default)]
pub whale_last_trade_at: Option<String>,
#[serde(default)]
pub end_date: Option<String>,
#[serde(default)]
pub created_at: Option<String>,
#[serde(default)]
pub outcome_yes: Option<String>,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the YES outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id_yes: Option<String>,
#[serde(default)]
pub outcome_no: Option<String>,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the NO outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id_no: Option<String>,
#[serde(default)]
pub event_slug: Option<String>,
#[serde(default)]
pub smart_score: Option<f64>,
#[serde(default)]
pub smart_count: Option<i64>,
/// The outcome graded money leans toward RELATIVE TO THE PRICE: named only when the graded money's share of a side diverges from that side's price-implied share by at least 10 points, with at least $500 on the leaning side and outside the crowded-side guard. Null when the money sits with the price (no lean), when there is no graded money, or when the price is unavailable. The same rule as the market page.
#[serde(default)]
pub smart_label: Option<String>,
/// When the oldest stored smart-money wallet behind this split (smart_score, smart_count and smart_label) last finished syncing. Graded wallets sync on different schedules, so this is often hours or days ago. Some holdings in the split can come from a Polymarket holder read cached for up to 3 minutes, which this time does not describe. Null when there is no split or a wallet's sync time is unknown.
#[serde(default)]
pub sharp_money_as_of: Option<String>,
/// Display label for the YES/outcome_index=0 side, enriched from provider outcome metadata when available.
#[serde(default)]
pub outcome_yes_label: Option<String>,
/// Display label for the NO/outcome_index=1 side, enriched from provider outcome metadata when available.
#[serde(default)]
pub outcome_no_label: Option<String>,
/// Provider-owned YES/outcome_index=0 identifier when available for trade-ticket wiring.
#[serde(default)]
pub outcome_yes_provider_id: Option<i64>,
/// Provider-owned NO/outcome_index=1 identifier when available for trade-ticket wiring.
#[serde(default)]
pub outcome_no_provider_id: Option<i64>,
#[serde(default)]
pub open_interest: Option<f64>,
#[serde(default)]
pub oi_change_pct: Option<f64>,
#[serde(default)]
pub price_points: Option<Vec<Vec<f64>>>,
#[serde(default)]
pub no_price_points: Option<Vec<Vec<f64>>>,
#[serde(default)]
pub last_price: Option<f64>,
#[serde(default)]
pub no_last_price: Option<f64>,
#[serde(default)]
pub change_pct_24h: Option<f64>,
#[serde(default)]
pub no_change_pct_24h: Option<f64>,
/// Backend-owned deterministic market discovery score used by the hot sort.
#[serde(default)]
pub discover_score: Option<f64>,
pub score_components: ExploreMarketScoreComponents,
pub freshness: ExploreMarketFreshness,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExploreMarketFreshness {
pub enrichment_status: EnrichmentStatus,
pub price_status: EnrichmentStatus,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExploreMarketScoreComponents {
#[serde(default)]
pub volume_signal: Option<f64>,
/// Canonical key since #16304; whale_signal is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub large_trade_signal: Option<f64>,
#[serde(default)]
pub whale_signal: Option<f64>,
#[serde(default)]
pub liquidity_signal: Option<f64>,
#[serde(default)]
pub recency_signal: Option<f64>,
/// Canonical key since #16308; smart_money_signal is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub sharp_money_signal: Option<f64>,
/// Deprecated spelling of sharp_money_signal; emitted beside it with the same value and never removed.
#[serde(default)]
pub smart_money_signal: Option<f64>,
#[serde(default)]
pub price_move_signal: Option<f64>,
pub missing_price_penalty: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExploreMarketsResponse {
pub object: String,
pub data: Vec<ExploreEntry>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching visible entries after grouping. Present on the first page and omitted on cursor pages.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub facets: ExploreFacets,
pub meta: ResponseMeta,
/// When this response body was computed. Present whenever the body came from, or was just written to, the 60s explore cache. Pair it with fresh_for_seconds to derive how much longer the body may be reused: fresh_for_seconds - age(computed_at). Excluded from the ETag validator, so a body recomputed with identical data keeps its validator.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub computed_at: Option<String>,
/// How long the body computed at computed_at is good for, in seconds. Deliberately not pre-subtracted: a cached body cannot carry a number that changes while it sits in the cache. Excluded from the ETag validator.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub fresh_for_seconds: Option<i64>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ExploreMarketsSort {
/// `trending`
#[serde(rename = "trending")]
Trending,
/// `hot`
#[serde(rename = "hot")]
Hot,
/// `expiring`
#[serde(rename = "expiring")]
Expiring,
/// `large_trades`
#[serde(rename = "large_trades")]
LargeTrades,
/// `whales`
#[serde(rename = "whales")]
Whales,
/// `volume`
#[serde(rename = "volume")]
Volume,
/// `newest`
#[serde(rename = "newest")]
Newest,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ExploreMarketsSort {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Trending => "trending",
Self::Hot => "hot",
Self::Expiring => "expiring",
Self::LargeTrades => "large_trades",
Self::Whales => "whales",
Self::Volume => "volume",
Self::Newest => "newest",
Self::Other(value) => value,
}
}
}
impl From<&str> for ExploreMarketsSort {
fn from(value: &str) -> Self {
match value {
"trending" => Self::Trending,
"hot" => Self::Hot,
"expiring" => Self::Expiring,
"large_trades" => Self::LargeTrades,
"whales" => Self::Whales,
"volume" => Self::Volume,
"newest" => Self::Newest,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ExploreMarketsSort {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ExploreMarketsSort {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExploreStandalone {
pub r#type: String,
pub market: ExploreMarket,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExportCompleteness {
pub status: SnapshotCompletenessStatus,
pub reason: String,
pub sync_coverage: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExportCounts {
/// Daily P&L rows. The daily P&L covers every market of the wallet.
pub pnl_days: i64,
/// The wallet's sports and esports markets: the rows of the export's markets section, before its 50,000-row cap.
pub exported_markets: i64,
/// Estimated trades in the wallet's sports and esports markets: the summed trade counts of those markets. The file counts its own rows in generation.source_watermarks.trades.rows.
pub estimated_trade_rows: i64,
/// Estimated file size from estimated_trade_rows and exported_markets.
pub estimated_size_mb: f64,
}
/// `export_job_cancelled` data: the owner's export stopped because its owner cancelled it.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExportJobCancelledData {
pub job_id: i64,
pub status: String,
pub format: String,
pub next_action: String,
}
/// `export_job_expired` data: the owner's ready export passed retention.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExportJobExpiredData {
pub job_id: i64,
pub status: String,
pub format: String,
pub next_action: String,
}
/// `export_job_failed` data: the owner's asynchronous export reached a terminal failure.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExportJobFailedData {
pub job_id: i64,
pub status: String,
pub format: String,
pub next_action: String,
pub failure_reason: String,
}
/// `export_job_ready` data: the owner's asynchronous export can be downloaded. Download authorization stays on the status and download routes.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExportJobReadyData {
pub job_id: i64,
pub status: String,
pub format: String,
pub next_action: String,
#[serde(default)]
pub total_trades: Option<i64>,
#[serde(default)]
pub processed_trades: Option<i64>,
/// Artifact size in bytes.
#[serde(default)]
pub file_size: Option<i64>,
#[serde(default)]
pub data_as_of: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExportSourceRange {
#[serde(default)]
pub first_pnl_date: Option<String>,
#[serde(default)]
pub last_pnl_date: Option<String>,
/// The latest trade in the wallet's sports and esports markets. Null when it has none.
#[serde(default)]
pub latest_trade_at: Option<String>,
/// Same instant as latest_trade_at: the latest activity of the markets the export holds.
#[serde(default)]
pub latest_market_activity_at: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ExportVolumeReconciliation {
/// Verified full-history both-sides USD cash volume. Null without coverage; the local activity numerator may cover only a subset of history.
#[serde(default)]
pub provider_lifetime_volume: Option<f64>,
/// Cash in USD, buys and sells, summed over the daily P&L rows this export carries, from the fills 0xinsider has stored for the wallet. It can cover less history than provider_lifetime_volume; never Polymarket's share count.
pub exported_activity_volume: f64,
/// Cost basis in USD summed over the wallet's sports and esports markets, the markets section of the export.
pub exported_market_cost_basis: f64,
/// provider_lifetime_volume minus exported_activity_volume, in USD, rounded to cents. Null when provider_lifetime_volume is null.
#[serde(default)]
pub provider_activity_volume_gap: Option<f64>,
/// exported_activity_volume divided by provider_lifetime_volume, a fraction of the cash, not a percent. Null when provider_lifetime_volume is null or zero.
#[serde(default)]
pub activity_volume_coverage: Option<f64>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Format {
/// `json`
#[serde(rename = "json")]
Json,
/// `ndjson`
#[serde(rename = "ndjson")]
Ndjson,
/// `csv`
#[serde(rename = "csv")]
Csv,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Format {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Json => "json",
Self::Ndjson => "ndjson",
Self::Csv => "csv",
Self::Other(value) => value,
}
}
}
impl From<&str> for Format {
fn from(value: &str) -> Self {
match value {
"json" => Self::Json,
"ndjson" => Self::Ndjson,
"csv" => Self::Csv,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Format {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Format {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Freshness {
/// `fresh`
#[serde(rename = "fresh")]
Fresh,
/// `refreshing`
#[serde(rename = "refreshing")]
Refreshing,
/// `stale`
#[serde(rename = "stale")]
Stale,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Freshness {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Fresh => "fresh",
Self::Refreshing => "refreshing",
Self::Stale => "stale",
Self::Unknown => "unknown",
Self::Other(value) => value,
}
}
}
impl From<&str> for Freshness {
fn from(value: &str) -> Self {
match value {
"fresh" => Self::Fresh,
"refreshing" => Self::Refreshing,
"stale" => Self::Stale,
"unknown" => Self::Unknown,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Freshness {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Freshness {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FreshnessFailure {
/// The caller's requested whole-response freshness ceiling in seconds.
pub max_age_s: i64,
/// Age in seconds of the oldest stored data_quality.as_of clock, when one is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub actual_age_s: Option<i64>,
/// The oldest stored data-quality clock used to calculate actual_age_s, when one is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub as_of: Option<String>,
/// The trader body's whole-response data-quality status. Only fresh can satisfy max_age_s.
pub data_quality_status: DataQualityGroupStatus,
}
/// One sports or esports game: both sides, its schedule, provider status, linked Polymarket markets and their available provider moneyline price states. Assembled from the same provider-first live and upcoming projections the site's boards use, with no request-time provider fan-out.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Game {
pub object: String,
/// The game's identity, for example mlb-mil-cin-2026-06-22. The same key the live_sports_updated webhook pulse carries and the same key /api/v1/games/{event_slug} takes.
pub event_slug: String,
/// The provider's Gamma gameId, as a string. Omitted when the canonical owner has no single value for this slug: the provider stamps one gameId across an event's derivative siblings, so an ambiguous read is reported as unknown rather than guessed.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub game_id: Option<String>,
/// The canonical sport bucket, for example Soccer or Table Tennis. Omitted when neither the board scope nor the provider category names one.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sport: Option<String>,
/// The league tag, for example nfl or epl. Omitted for a sport served as one whole bucket with no league scope.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub league: Option<String>,
/// The provider's event title. Omitted when the provider sent none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
/// Kickoff in UTC, as the provider supplied it. Omitted when the provider published none; coverage.schedule then reads unavailable.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scheduled_at: Option<String>,
pub status: GameStatus,
/// Both sides, in the provider's own order. For a team league the provider lists the home side first. Empty when the provider identified neither side.
pub competitors: Vec<GameCompetitor>,
/// The esports series length, for example Bo3. Omitted for everything else.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub series_format: Option<String>,
/// Whether this read includes a market classified as paying on a draw, including soccer's independent 1X2 draw leg. Read this instead of assuming a two-outcome moneyline.
pub draw_offered: bool,
/// Every market this read linked to the game, ordered by condition_id.
pub markets: Vec<GameMarket>,
pub freshness: GameFreshness,
pub coverage: GameCoverage,
/// The game's page on 0xinsider.
pub url: String,
}
/// One side of the game.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameCompetitor {
/// The competitor's name as the provider gives it.
pub name: String,
/// The provider's league-scoped competitor id, as a string. Omitted when the provider has not identified this side; coverage.competitors then reads labels.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_id: Option<String>,
/// Provider crest or logo URL. Omitted when there is none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub logo: Option<String>,
/// The provider's score for this side, verbatim. A string because the provider sends one: a set score, a map score and a run total are not all integers. Omitted when no live-score frame carries a score.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub score: Option<String>,
/// The provider's season record for this side, for example 12-4. Omitted when the provider sent none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub record: Option<String>,
}
/// What this game's read actually supplied, so a client branches on coverage instead of on a missing key.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameCoverage {
/// available when a live-score frame supplied this game's scores.
pub scores: EnrichmentStatus,
/// provider_ids when every side carries a provider id, labels when only the provider's names identify them, unavailable when neither exists. Do not join on names when this reads labels.
pub competitors: Competitors,
/// available when the provider supplied a kickoff.
pub schedule: EnrichmentStatus,
}
/// How current this game's facts are. Independent per source: the board half that produced the game, and the live-score frame that produced its scores.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameFreshness {
/// Which board half produced this game.
pub source: Source,
/// Whether that half returned a truthful source body for this read.
pub source_status: SourceStatus,
/// Whether that half had a source body at all. not_applicable means the sport has no configured source for that half.
pub source_availability: SourceAvailability,
/// Freshness of the cached source body, never inferred from the response clock or the row count.
pub source_freshness: SourceFreshness,
/// The source body's data vintage. Omitted when the read has no vintage anchor, which is not age zero.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_observed_at: Option<String>,
/// Age of source_observed_at in seconds, capped at 600. Omitted past the cap or with no anchor.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_age_seconds: Option<i64>,
/// Whether a reader should be told these rows are behind. This applies the half's own servable-age bar (15 s live, 120 s upcoming), which is not the same as source_freshness: a live board whose scores are seconds old reads stale for about half of every publish cycle and is not delayed.
pub delayed: bool,
/// When this game's live-score frame was observed. Omitted when there is no frame.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scores_observed_at: Option<String>,
/// The provider's own frame clock. Omitted when the frame carries none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scores_source_at: Option<String>,
}
/// One Polymarket market linked to this game.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameMarket {
/// The mkt_-prefixed market id every other V1 response uses.
pub id: String,
/// The raw provider condition id.
pub condition_id: String,
/// Always polymarket.
pub platform: String,
/// The provider's market slug. Omitted when the provider sent none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
/// The provider's own market type, for example moneyline or spread. Omitted when the provider sent none. Not an enum: the provider owns this vocabulary and adds to it.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sports_market_type: Option<String>,
/// Which side of the game this market's YES leg pays. Soccer 1X2 legs are explicitly classified as home, draw or away by the provider projection. draw is a real value, not a competitor. Omitted when the provider projection cannot classify the leg, which is not the same as other.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub side: Option<GameMarketSide>,
/// The provider's label for the YES outcome. Omitted when the provider sent none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_yes: Option<String>,
/// The provider's label for the NO outcome. Omitted when the provider sent none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_no: Option<String>,
/// Polymarket CLOB token ids in the provider's own outcome-index order. Omitted when the provider has published none for this market.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_token_ids: Option<Vec<String>>,
/// Default provider moneyline state. Omitted when this market has no classified moneyline projection; incomplete and invalid projections remain explicit.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub prices: Option<GameMarketPrices>,
}
/// How the existing sports-board writer bound prices to competitors.
///
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GameMarketPriceBindingProvenance {
/// `provider_ids`
#[serde(rename = "provider_ids")]
ProviderIds,
/// `exact_labels`
#[serde(rename = "exact_labels")]
ExactLabels,
/// `containment_labels`
#[serde(rename = "containment_labels")]
ContainmentLabels,
/// `elimination`
#[serde(rename = "elimination")]
Elimination,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl GameMarketPriceBindingProvenance {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::ProviderIds => "provider_ids",
Self::ExactLabels => "exact_labels",
Self::ContainmentLabels => "containment_labels",
Self::Elimination => "elimination",
Self::Other(value) => value,
}
}
}
impl From<&str> for GameMarketPriceBindingProvenance {
fn from(value: &str) -> Self {
match value {
"provider_ids" => Self::ProviderIds,
"exact_labels" => Self::ExactLabels,
"containment_labels" => Self::ContainmentLabels,
"elimination" => Self::Elimination,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for GameMarketPriceBindingProvenance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for GameMarketPriceBindingProvenance {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// One competitor-bound provider moneyline price. No YES/NO inference is required.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameMarketPriceCompetitor {
/// The provider competitor id when identity is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_id: Option<i64>,
/// The provider competitor label.
pub label: String,
/// Unrounded provider price in [0, 1].
pub price: f64,
/// Which provider price observation supplies this leg.
pub price_provenance: PriceProvenance,
}
/// The provider moneyline pair is incomplete; no numeric pair is invented.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameMarketPriceIncomplete {
pub state: String,
pub reason: Reason,
/// Whether competitor A is the provider YES leg.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub competitor_a_is_yes: Option<bool>,
/// The provider competitor id when identity is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub competitor_a_provider_id: Option<i64>,
/// The provider competitor id when identity is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub competitor_b_provider_id: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub binding_provenance: Option<GameMarketPriceBindingProvenance>,
}
/// The provider moneyline pair is invalid; no numeric pair is invented.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameMarketPriceInvalid {
pub state: String,
pub reason: GameMarketPriceInvalidReason,
/// Whether competitor A is the provider YES leg.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub competitor_a_is_yes: Option<bool>,
/// The provider competitor id when identity is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub competitor_a_provider_id: Option<i64>,
/// The provider competitor id when identity is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub competitor_b_provider_id: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub binding_provenance: Option<GameMarketPriceBindingProvenance>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GameMarketPriceInvalidReason {
/// `team_cardinality`
#[serde(rename = "team_cardinality")]
TeamCardinality,
/// `outcome_cardinality`
#[serde(rename = "outcome_cardinality")]
OutcomeCardinality,
/// `price_cardinality`
#[serde(rename = "price_cardinality")]
PriceCardinality,
/// `non_finite_price`
#[serde(rename = "non_finite_price")]
NonFinitePrice,
/// `out_of_range_price`
#[serde(rename = "out_of_range_price")]
OutOfRangePrice,
/// `non_complementary_prices`
#[serde(rename = "non_complementary_prices")]
NonComplementaryPrices,
/// `ambiguous_identity`
#[serde(rename = "ambiguous_identity")]
AmbiguousIdentity,
/// `final_identity_mismatch`
#[serde(rename = "final_identity_mismatch")]
FinalIdentityMismatch,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl GameMarketPriceInvalidReason {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::TeamCardinality => "team_cardinality",
Self::OutcomeCardinality => "outcome_cardinality",
Self::PriceCardinality => "price_cardinality",
Self::NonFinitePrice => "non_finite_price",
Self::OutOfRangePrice => "out_of_range_price",
Self::NonComplementaryPrices => "non_complementary_prices",
Self::AmbiguousIdentity => "ambiguous_identity",
Self::FinalIdentityMismatch => "final_identity_mismatch",
Self::Other(value) => value,
}
}
}
impl From<&str> for GameMarketPriceInvalidReason {
fn from(value: &str) -> Self {
match value {
"team_cardinality" => Self::TeamCardinality,
"outcome_cardinality" => Self::OutcomeCardinality,
"price_cardinality" => Self::PriceCardinality,
"non_finite_price" => Self::NonFinitePrice,
"out_of_range_price" => Self::OutOfRangePrice,
"non_complementary_prices" => Self::NonComplementaryPrices,
"ambiguous_identity" => Self::AmbiguousIdentity,
"final_identity_mismatch" => Self::FinalIdentityMismatch,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for GameMarketPriceInvalidReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for GameMarketPriceInvalidReason {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A validated provider moneyline pair bound to the two competitors.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameMarketPricePaired {
pub state: String,
pub competitor_a: GameMarketPriceCompetitor,
pub competitor_b: GameMarketPriceCompetitor,
/// Whether competitor A is the provider YES leg.
pub competitor_a_is_yes: bool,
pub binding_provenance: GameMarketPriceBindingProvenance,
}
/// Provider-owned moneyline state with the observation clock that can be compared with game freshness.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameMarketPrices {
pub provider: GameMarketProviderPrices,
/// Board cache vintage for Gamma or the older CLOB leg clock. Null when no reliable clock is available; never a Gamma-authored timestamp.
#[serde(default)]
pub observed_at: Option<String>,
/// The clock used for observed_at. board_snapshot is this service’s cache observation, not a provider source timestamp.
pub observation_source: ObservationSource,
}
/// Discriminated by `state`. A `state` this release does not know is kept whole in
/// `Unknown`, so a new kind never fails a response.
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum GameMarketProviderPrices {
/// `state` is `paired`.
Paired(GameMarketPricePaired),
/// `state` is `incomplete`.
Incomplete(GameMarketPriceIncomplete),
/// `state` is `invalid`.
Invalid(GameMarketPriceInvalid),
/// A `state` this release does not know, kept as sent.
Unknown(serde_json::Value),
}
impl<'de> Deserialize<'de> for GameMarketProviderPrices {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = serde_json::Value::deserialize(deserializer)?;
match value.get("state").and_then(serde_json::Value::as_str) {
Some("paired") => serde_json::from_value(value)
.map(Self::Paired)
.map_err(de::Error::custom),
Some("incomplete") => serde_json::from_value(value)
.map(Self::Incomplete)
.map_err(de::Error::custom),
Some("invalid") => serde_json::from_value(value)
.map(Self::Invalid)
.map_err(de::Error::custom),
_ => Ok(Self::Unknown(value)),
}
}
}
impl Serialize for GameMarketProviderPrices {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Paired(inner) => inner.serialize(serializer),
Self::Incomplete(inner) => inner.serialize(serializer),
Self::Invalid(inner) => inner.serialize(serializer),
Self::Unknown(inner) => inner.serialize(serializer),
}
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GameMarketSide {
/// `home`
#[serde(rename = "home")]
Home,
/// `away`
#[serde(rename = "away")]
Away,
/// `draw`
#[serde(rename = "draw")]
Draw,
/// `other`, the documented value; `Other` holds values this release does not know.
#[serde(rename = "other")]
OtherValue,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl GameMarketSide {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Home => "home",
Self::Away => "away",
Self::Draw => "draw",
Self::OtherValue => "other",
Self::Other(value) => value,
}
}
}
impl From<&str> for GameMarketSide {
fn from(value: &str) -> Self {
match value {
"home" => Self::Home,
"away" => Self::Away,
"draw" => Self::Draw,
"other" => Self::OtherValue,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for GameMarketSide {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for GameMarketSide {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Where the game is in its own life, as the provider reports it. A postponement, a cancellation and a suspension each keep their own state, so a client can tell a game that will be played later from one that never will be.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GameStatus {
/// scheduled: kickoff is ahead or the provider still calls it scheduled. live: the provider reports it in play. paused: halftime or a provider-reported break. ended: the provider reported a final, an award or a forfeit. postponed, cancelled, suspended, delayed: the provider's own verdict, kept distinct. unknown: no provider state reached this read. A kickoff in the past is never read as live on its own.
pub state: State,
/// The provider status folded onto one vocabulary across leagues. unknown means the provider sent a value this API has no meaning for; provider_status keeps that value verbatim. Omitted when no live-score frame carries a status.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub match_status: Option<MatchStatus>,
/// The provider's status string, verbatim. Omitted when the provider sent none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_status: Option<String>,
/// The provider's period label, for example Q3, End Q2 or T5. Omitted when the provider sent none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub period: Option<String>,
/// The game clock as the provider spells it, never reformatted. Omitted when the provider sent none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clock: Option<String>,
/// Whether the game is in play. It is the provider's own in-play flag, except that a game the provider reports suspended (period SUS) or rescheduled to a later kickoff reads false. Omitted when no live-score frame exists, which is not the same as false.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub live: Option<bool>,
/// Whether the game is over. Always present.
pub ended: bool,
}
/// What this deployment covers, published with every page so a client never has to guess whether an empty list means no games or no coverage.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GamesCoverage {
/// Canonical sport buckets served, sorted.
pub sports: Vec<String>,
/// League tags served, sorted. EuroLeague is euroleague under the Basketball sport bucket.
pub leagues: Vec<String>,
/// Scopes whose source half was unavailable for this read, as <sport>:<half>. Empty means every scope answered.
pub sources_unavailable: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetApiDiscoveryResponse {
pub object: String,
pub data: ApiDiscovery,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetCoverageResponse {
pub object: String,
pub data: Platforms,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetDailyReportSnapshotResponse {
pub object: String,
pub data: ReportSnapshot,
pub meta: ResponseMeta,
}
/// Only trades of at least this USD amount. Decimal and scientific query spellings normalize exactly to cents, rounding half away from zero; maximum 1e15 USD. Pass the dataset continuation decimal string unchanged to preserve the bound. Bound to the cursor.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[non_exhaustive]
pub enum GetEventReplaySinceMinSize {
/// A numeric request value.
Number(f64),
/// The exact decimal query spelling, kept unchanged.
Text(String),
}
impl From<f64> for GetEventReplaySinceMinSize {
fn from(value: f64) -> Self {
Self::Number(value)
}
}
impl From<String> for GetEventReplaySinceMinSize {
fn from(value: String) -> Self {
Self::Text(value)
}
}
impl From<&str> for GetEventReplaySinceMinSize {
fn from(value: &str) -> Self {
Self::Text(value.to_owned())
}
}
impl std::fmt::Display for GetEventReplaySinceMinSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Number(value) => value.fmt(f),
Self::Text(value) => value.fmt(f),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetEventReplaySinceResponse {
pub object: String,
pub data: Vec<EventReplayEvent>,
pub has_more: bool,
pub next_cursor: String,
pub meta: EventReplayMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetGameResponse {
pub object: String,
pub data: Game,
/// When this read assembled the catalog. Per-source vintage is on freshness.
pub as_of: String,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetHealthResponse {
pub object: String,
pub data: GetHealthResponseData,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetHealthResponseData {
pub status: GetHealthResponseDataStatus,
pub db: bool,
pub cache: bool,
pub subsystems: GetHealthResponseDataSubsystems,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GetHealthResponseDataStatus {
/// `ok`
#[serde(rename = "ok")]
Ok,
/// `degraded`
#[serde(rename = "degraded")]
Degraded,
/// `down`
#[serde(rename = "down")]
Down,
/// `maintenance`
#[serde(rename = "maintenance")]
Maintenance,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl GetHealthResponseDataStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Ok => "ok",
Self::Degraded => "degraded",
Self::Down => "down",
Self::Maintenance => "maintenance",
Self::Other(value) => value,
}
}
}
impl From<&str> for GetHealthResponseDataStatus {
fn from(value: &str) -> Self {
match value {
"ok" => Self::Ok,
"degraded" => Self::Degraded,
"down" => Self::Down,
"maintenance" => Self::Maintenance,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for GetHealthResponseDataStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for GetHealthResponseDataStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetHealthResponseDataSubsystems {
/// Whether the webhook outbox schema validated on this probe. false lowers status to degraded. true during a maintenance window, when nothing is probed.
pub webhook_compatibility: bool,
/// Public-safe aggregate status for expected background job successful-completion heartbeats. Does not expose Redis keys, job names, raw errors, provider payloads, wallet addresses, or condition IDs.
pub background_jobs: GetHealthResponseDataSubsystemsBackgroundJobs,
}
/// Public-safe aggregate status for expected background job successful-completion heartbeats. Does not expose Redis keys, job names, raw errors, provider payloads, wallet addresses, or condition IDs.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetHealthResponseDataSubsystemsBackgroundJobs {
pub status: GetHealthResponseDataSubsystemsBackgroundJobsStatus,
pub checked: bool,
pub total: i64,
pub healthy: i64,
pub stale: i64,
pub missing: i64,
pub invalid: i64,
pub unconfigured: i64,
/// Heavy-periodic jobs that are stale/missing/invalid while the heavy executor is disabled (heavy_pipeline_control.executor_mode != 'active'), so this binary does not dispatch them. Observable but expected; they never drive status to 'down' (paused > 0 yields 'degraded').
pub paused: i64,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GetHealthResponseDataSubsystemsBackgroundJobsStatus {
/// `ok`
#[serde(rename = "ok")]
Ok,
/// `degraded`
#[serde(rename = "degraded")]
Degraded,
/// `down`
#[serde(rename = "down")]
Down,
/// `not_checked`
#[serde(rename = "not_checked")]
NotChecked,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl GetHealthResponseDataSubsystemsBackgroundJobsStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Ok => "ok",
Self::Degraded => "degraded",
Self::Down => "down",
Self::NotChecked => "not_checked",
Self::Other(value) => value,
}
}
}
impl From<&str> for GetHealthResponseDataSubsystemsBackgroundJobsStatus {
fn from(value: &str) -> Self {
match value {
"ok" => Self::Ok,
"degraded" => Self::Degraded,
"down" => Self::Down,
"not_checked" => Self::NotChecked,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for GetHealthResponseDataSubsystemsBackgroundJobsStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for GetHealthResponseDataSubsystemsBackgroundJobsStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetInsiderRadarFlagResponse {
pub object: String,
pub data: SuspiciousTrade,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetLargeTradeResponse {
pub object: String,
pub data: LargeTradeDetail,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetMarketCandlesResponse {
pub object: String,
pub data: MarketCandles,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetMarketFlowResponse {
pub object: String,
pub data: MarketFlow,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GetMarketHoldersOutcome {
/// `yes`
#[serde(rename = "yes")]
Yes,
/// `no`
#[serde(rename = "no")]
No,
/// `all`
#[serde(rename = "all")]
All,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl GetMarketHoldersOutcome {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Yes => "yes",
Self::No => "no",
Self::All => "all",
Self::Other(value) => value,
}
}
}
impl From<&str> for GetMarketHoldersOutcome {
fn from(value: &str) -> Self {
match value {
"yes" => Self::Yes,
"no" => Self::No,
"all" => Self::All,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for GetMarketHoldersOutcome {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for GetMarketHoldersOutcome {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetMarketHoldersResponse {
pub object: String,
pub data: Vec<MarketHolder>,
pub has_more: bool,
/// Absent on the last page.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Holders matching the request's `outcome` and `min_grade` filters across every page.
pub total: i64,
pub market: MarketHoldersMarket,
pub scan: MarketHoldersScan,
pub totals: MarketHoldersTotals,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetMarketIntelResponse {
pub object: String,
pub data: MarketFlow,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetMarketSnapshotResponse {
pub object: String,
pub data: MarketSnapshot,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetMonthlyReportSnapshotResponse {
pub object: String,
pub data: ReportSnapshot,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetPickOfTheDayArchiveResponse {
pub object: String,
pub data: PickOfTheDayArchive,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetPickOfTheDayLedgerEntryResponse {
pub object: String,
pub data: PickOfTheDayLedgerEntry,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetPickOfTheDayLedgerResponse {
pub object: String,
pub data: PickOfTheDayLedger,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetPickOfTheDayResponse {
pub object: String,
pub data: serde_json::Value,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetPlatformsResponse {
pub object: String,
pub data: Platforms,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetPositionTimelineByIdResponse {
pub object: String,
pub data: Vec<PositionTimelineEvent>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetPositionTimelineResponse {
pub object: String,
pub data: Vec<PositionTimelineEvent>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetReportsResponse {
pub object: String,
pub data: ReportSnapshot,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetSuspiciousTradeResponse {
pub object: String,
pub data: SuspiciousTrade,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetTraderCategoryRecordsResponse {
pub object: String,
pub data: TraderCategoryRecords,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetTraderContextResponse {
pub object: String,
pub data: TraderContext,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetTraderExportSnapshotResponse {
pub object: String,
pub data: TraderExportSnapshot,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetTraderGradeAtResponse {
pub object: String,
pub data: TraderGradeAt,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetTraderPnlResponse {
pub object: String,
pub data: TraderPnl,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetTraderResponse {
pub object: String,
pub data: Trader,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetWebhookResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetWebhookVerificationAttemptResponse {
pub object: String,
pub data: WebhookVerificationAttempt,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetWeeklyReportSnapshotResponse {
pub object: String,
pub data: ReportSnapshot,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetWhaleTradeResponse {
pub object: String,
pub data: LargeTradeDetail,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Grade {
/// `S`
#[serde(rename = "S")]
S,
/// `A`
#[serde(rename = "A")]
A,
/// `B`
#[serde(rename = "B")]
B,
/// `C`
#[serde(rename = "C")]
C,
/// `D`
#[serde(rename = "D")]
D,
/// `F`
#[serde(rename = "F")]
F,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Grade {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::S => "S",
Self::A => "A",
Self::B => "B",
Self::C => "C",
Self::D => "D",
Self::F => "F",
Self::Other(value) => value,
}
}
}
impl From<&str> for Grade {
fn from(value: &str) -> Self {
match value {
"S" => Self::S,
"A" => Self::A,
"B" => Self::B,
"C" => Self::C,
"D" => Self::D,
"F" => Self::F,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Grade {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Grade {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GradeAtTradeStatus {
/// `graded`
#[serde(rename = "graded")]
Graded,
/// `ungraded`
#[serde(rename = "ungraded")]
Ungraded,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl GradeAtTradeStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Graded => "graded",
Self::Ungraded => "ungraded",
Self::Unknown => "unknown",
Self::Other(value) => value,
}
}
}
impl From<&str> for GradeAtTradeStatus {
fn from(value: &str) -> Self {
match value {
"graded" => Self::Graded,
"ungraded" => Self::Ungraded,
"unknown" => Self::Unknown,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for GradeAtTradeStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for GradeAtTradeStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Granularity {
/// `daily`
#[serde(rename = "daily")]
Daily,
/// `weekly`
#[serde(rename = "weekly")]
Weekly,
/// `monthly`
#[serde(rename = "monthly")]
Monthly,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Granularity {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Daily => "daily",
Self::Weekly => "weekly",
Self::Monthly => "monthly",
Self::Other(value) => value,
}
}
}
impl From<&str> for Granularity {
fn from(value: &str) -> Self {
match value {
"daily" => Self::Daily,
"weekly" => Self::Weekly,
"monthly" => Self::Monthly,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Granularity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Granularity {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Current category evidence, independent of the global grade. Pick of the Day stamps only the served display roster; frozen entry snapshots remain unchanged.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct HolderCategoryEvidence {
pub status: Status,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub canonical_category: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub skill: Option<CategorySkillV2>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum IdentityStatus {
/// `resolved`
#[serde(rename = "resolved")]
Resolved,
/// `infrastructure`
#[serde(rename = "infrastructure")]
Infrastructure,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl IdentityStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Resolved => "resolved",
Self::Infrastructure => "infrastructure",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for IdentityStatus {
fn from(value: &str) -> Self {
match value {
"resolved" => Self::Resolved,
"infrastructure" => Self::Infrastructure,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for IdentityStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for IdentityStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Kind {
/// `sports`
#[serde(rename = "sports")]
Sports,
/// `esports`
#[serde(rename = "esports")]
Esports,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Kind {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Sports => "sports",
Self::Esports => "esports",
Self::Other(value) => value,
}
}
}
impl From<&str> for Kind {
fn from(value: &str) -> Self {
match value {
"sports" => Self::Sports,
"esports" => Self::Esports,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Kind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Kind {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeExportPolicy {
pub mode: String,
pub current_internal_route: String,
/// Programmatic API-key-gated export routes (submit/status/download/cancel) and supported formats (json, ndjson, csv).
pub v1_async: LargeExportPolicyV1Async,
pub direct_streaming: serde_json::Map<String, serde_json::Value>,
pub async_job: serde_json::Map<String, serde_json::Value>,
pub rate_limit: serde_json::Map<String, serde_json::Value>,
}
/// Programmatic API-key-gated export routes (submit/status/download/cancel) and supported formats (json, ndjson, csv).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeExportPolicyV1Async {
/// POST route template to submit an export job. fresh=true asks for a snapshot read after the submit instead of reusing a finished one.
pub submit_route: String,
/// GET route template to poll job status.
pub status_route: String,
/// GET route template that 302-redirects to the file.
pub download_route: String,
/// POST route template to cancel a queued or running job.
pub cancel_route: String,
/// Supported ?format= values.
pub formats: Vec<Format>,
/// Possible job status values.
pub status_values: Vec<TraderExportJobDataStatus>,
/// How long a finished export is retained before expiry.
pub retention: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargePosition {
/// Composite prefixed ID `pos_<wallet>:<condition_id>:<outcome_index>`.
pub id: String,
/// Provider discriminator. Always polymarket.
pub platform: String,
/// Provider currentValue in USD for the outcome named by outcome_label / token_id, and for that outcome only. An absent key means the provider supplied no value for this leg, not a zero position. combined_value_usd carries the wallet's whole-market total.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_size_usd: Option<f64>,
/// Provider unrealized mark-to-market P&L (Polymarket cashPnl) for that same one outcome. An absent key means unavailable, not break-even.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub position_unrealized_pnl: Option<f64>,
/// The wallet's currentValue across every outcome it holds in this market. Equals total_size_usd unless is_two_sided is true, and is the value the feed ranked this row by.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub combined_value_usd: Option<f64>,
/// The wallet's unrealized P&L across every outcome it holds in this market. Equals position_unrealized_pnl unless is_two_sided is true.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub combined_unrealized_pnl: Option<f64>,
/// Whether the wallet holds shares on both outcomes of this market.
pub is_two_sided: bool,
/// Every outcome the wallet holds, primary first. holdings[0] restates the top-level per-outcome fields; a further entry is a leg those fields do not describe. No amount here is a share-proportional slice of a both-sides total.
pub holdings: Vec<LargePositionHoldingsItem>,
/// Live share count for this position.
pub share_count: f64,
/// Volume-weighted entry price.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub avg_entry_price: Option<f64>,
/// Latest provider mark price for the position's token.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub current_price: Option<f64>,
/// Backend-resolved outcome label (provider outcome, else Yes/No from the binary index).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_label: Option<String>,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for this outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Legs collapsed into this representative row for one (wallet, event, outcome side) group. 1 means standalone.
pub event_leg_count: i64,
/// Sum of the collapsed sibling legs' own holding values for this (wallet, event) group; equals total_size_usd when event_leg_count = 1, and omitted when any member's value is unknown.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_total_value_usd: Option<f64>,
pub first_seen_at: String,
pub last_updated_at: String,
pub trader: LargePositionTrader,
pub market: LargePositionMarket,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargePositionHoldingsItem {
/// 0 = Yes, 1 = No.
pub outcome_index: i64,
/// The market's label for this outcome.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_label: Option<String>,
/// Polymarket CLOB token id for this outcome; null for an unsynced market.
#[serde(default)]
pub token_id: Option<String>,
/// Shares held on this outcome.
pub share_count: f64,
/// Provider average entry price for this outcome.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub avg_entry_price: Option<f64>,
/// Provider mark price for this outcome.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub current_price: Option<f64>,
/// Provider currentValue for this outcome. An absent key means unavailable, not zero.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value_usd: Option<f64>,
/// Provider unrealized P&L for this outcome. An absent key means unavailable, not break-even.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unrealized_pnl: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargePositionMarket {
/// Prefixed ID (`mkt_...`).
pub id: String,
pub condition_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_slug: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargePositionTrader {
/// Prefixed ID (`trd_...`).
pub id: String,
pub address: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grade: Option<Grade>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub win_rate: Option<f64>,
/// Polymarket's net total P&L for the wallet in USD: native Polymarket position P&L across all markets, fees included, plus credited maker and taker rebates. The 0xinsider profile's Total P&L is this figure unless the wallet has a published correction for neg-risk conversions, which GET /api/v1/trader/{address} serves as pnl.correction; the profile then shows pnl.correction.net_total. Distinct from this position's position_unrealized_pnl. Omitted when the wallet has no native accounting snapshot.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pnl: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub markets_traded: Option<i64>,
}
/// `large_positions_updated` data: one large-positions refresh finished.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargePositionsUpdatedData {
/// Positions the refresh published.
pub count: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTrade {
/// Prefixed ID (wt_...).
pub id: String,
pub traded_at: String,
pub size_usd: f64,
pub side: LargeTradeInsertedV2DataSide,
/// Traded outcome label (e.g. "Yes"/"No"/team name), resolved provider-first from the trade's outcome_index against market_canonical (index 0 -> yes, 1 -> no). Distinct axis from side (BUY/SELL): side is the trade direction, outcome is which leg was traded. null for multi-outcome (outcome_index >= 2) or unsynced markets, and for a Polymarket trade recorded before 2026-04-02T00:00:00Z unless its stored outcome_index was verified against the fill's own trade records (before that date the stored index was wrong for a large share of trades; an unverified side is unknown, not defaulted).
#[serde(default)]
pub outcome: Option<String>,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the traded outcome; null when unavailable (e.g. unsynced markets) and for a Polymarket trade recorded before 2026-04-02T00:00:00Z whose traded side is not verified (the same rule as outcome).
#[serde(default)]
pub token_id: Option<String>,
pub price: f64,
/// Current trade review score on a 0..1 scale; higher values indicate a stronger review signal. This is the current response value and can differ from the recorded score. Missing measurements remain unavailable.
pub review_score: f64,
/// Deprecated alias of review_score with the same current value and 0..1 scale.
pub signal_score: f64,
/// 0.0–1.0 review score written once when the trade row is inserted, from the trader's statistics at that moment. Populated from 2026-08-03T11:59Z; older rows return null and are never backfilled, because a backfill could only read today's statistics. If a trade is added later, its time-sensitive recorded score reflects that delay. Canonical since #16311; recorded_signal_score carries the same value.
#[serde(default)]
pub recorded_review_score: Option<f64>,
/// 0.0–1.0 review score written once when the trade row is inserted; null before 2026-08-03T11:59Z. Deprecated (#16311): `recorded_review_score` is the canonical spelling and carries the same value; this key stays on the wire.
#[serde(default)]
pub recorded_signal_score: Option<f64>,
/// Persisted live suspicion score from the scorer. Null when the row has no persisted score.
#[serde(default)]
pub suspicion_score: Option<i64>,
/// Persisted scorer track. Null when a legacy row has no stored track label.
#[serde(default)]
pub suspicion_track: Option<SuspicionTrack>,
/// This fill's size relative to its market: its share count (size_usd / price) divided by the shares the market has traded, Polymarket's recorded volume, which counts shares rather than dollars, as observed at or after the trade, so the value always falls between 0 and 1 inclusive. A 20,000-share fill is 0.00005 of a market that has traded 400M shares and 0.125 of one that has traded 160,000, which size_usd alone cannot distinguish. Absent when no volume figure recorded at or after the trade is available, or when that figure is smaller than the fill; never 0 as a stand-in and never capped at 1, because a denominator we cannot trust publishes nothing rather than a trimmed number. A market's volume keeps growing, so the same trade reports a smaller share as the market trades on.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub market_volume_share: Option<f64>,
pub trader: LargeTradeTrader,
pub market: LargeTradeMarket,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTradeDetail {
#[serde(flatten)]
pub large_trade: LargeTrade,
pub counterparty_analysis: CounterpartyAnalysis,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTradeHistoryMeta {
/// Unique request ID (req_ prefix). The same value as the X-Request-Id response header, the request's usage accounting row and its log lines.
pub request_id: String,
pub cached: bool,
/// Cache age in seconds; the key is absent when the response was not cached.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_age_s: Option<i64>,
pub source: LargeTradeHistoryMetaSource,
pub completeness: LargeTradeHistoryMetaCompleteness,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTradeHistoryMetaCompleteness {
pub status: String,
/// Explains that local replay completeness can vary by market and time window.
pub reason: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTradeHistoryMetaSource {
pub kind: String,
pub table: String,
pub provider_fetch_at_request_time: bool,
}
/// `large_trade_inserted_v2` data (Pro and Max): one large trade, with the trade itself. The three amounts are decimal strings that keep the stored precision: parse them with a decimal library when the exact value matters. The endpoint's `trade_filters` decide which trades it receives.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTradeInsertedV2Data {
/// Stable identity of this trade's event. It equals the envelope `id`, and the `id` of the same event on `GET /api/v1/events/feed/since`.
pub event_id: String,
/// Numeric id of the trade. `GET /api/v1/large-trades/{id}` accepts it.
pub whale_alert_id: i64,
/// Polymarket condition id of the market.
pub condition_id: String,
/// 0xinsider's numeric id for the wallet.
pub trader_id: i64,
/// Address of the wallet that made the trade.
pub wallet: String,
/// The wallet's grade when the event was published; `null` when it has none.
#[serde(default)]
pub grade: Option<Grade>,
/// USD notional of the trade; decimal string.
pub size_usd: String,
/// Shares traded; decimal string.
pub shares: String,
/// Price paid per share; decimal string.
pub price: String,
pub side: LargeTradeInsertedV2DataSide,
/// Polymarket's index of the outcome traded.
pub outcome_index: i64,
/// When the trade filled on Polymarket. The envelope's `created_at` is this instant.
pub traded_at: String,
pub platform: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum LargeTradeInsertedV2DataSide {
/// `BUY`
#[serde(rename = "BUY")]
Buy,
/// `SELL`
#[serde(rename = "SELL")]
Sell,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl LargeTradeInsertedV2DataSide {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Buy => "BUY",
Self::Sell => "SELL",
Self::Other(value) => value,
}
}
}
impl From<&str> for LargeTradeInsertedV2DataSide {
fn from(value: &str) -> Self {
match value {
"BUY" => Self::Buy,
"SELL" => Self::Sell,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for LargeTradeInsertedV2DataSide {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for LargeTradeInsertedV2DataSide {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTradeMarket {
pub id: String,
pub condition_id: String,
pub title: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
/// Provider-backed market_canonical category.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
}
/// All present fields narrow large_trade_inserted_v2 delivery. Grade is observed at publication; ungraded trades do not match min_grade. An empty object matches every large trade.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct LargeTradeSubscriptionFilters {
/// Raw provider condition ID or mkt_-prefixed market ID.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition_id: Option<String>,
/// Polymarket wallet address; matching is case-insensitive.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub wallet: Option<String>,
/// S is best; ungraded trades do not match.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min_grade: Option<Grade>,
/// Positive USD notional as an exact decimal string.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min_size_usd: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTradeTrader {
pub id: String,
pub address: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
/// The trader's grade today, on every row however old. For what the grade was when the trade happened, read grade_at_trade.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grade: Option<String>,
/// The grade the trader held when the trade happened, from recorded grade history (recorded from 2026-09-19T23:00Z). Null unless grade_at_trade_status is graded. Never today's grade projected backward.
#[serde(default)]
pub grade_at_trade: Option<Grade>,
/// graded: grade_at_trade holds the recorded grade. ungraded: the trader was recorded without a grade at that moment. unknown: no record covers the moment, which is every trade before 2026-09-19T23:00Z and a trade that fell between a grade change and its confirmation. unknown never means ungraded.
pub grade_at_trade_status: GradeAtTradeStatus,
}
/// `large_trades_inserted` data (Pro and Max): new large trades were ingested. It carries a count and no trade; subscribe to `large_trade_inserted_v2` for the trade itself. The deprecated spelling `whale_trades_inserted` delivers the same object.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LargeTradesInsertedData {
/// Large trades the producing batch admitted.
pub count: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LeaderboardEntry {
pub id: String,
pub address: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grade: Option<String>,
/// Hot-streak tier (trailing-7d cross-sectional percentile); a separate axis from the all-time grade. Omitted when there is no recent activity.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub streak_tier: Option<StreakTier>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub score: Option<f64>,
/// Polymarket's all-time leaderboard P&L in USD, from Polymarket's latest observation of this wallet on its all-time leaderboard (dated by the leaderboard_pnl data_quality group). Omitted when that leaderboard has no current observation ranking the wallet; 0xinsider requests one, and never serves an older stored figure in its place. Includes unrealized open positions. Kept for back-compat. Not the profile's Total P&L, which GET /api/v1/trader/{address} serves as pnl.net_total; the two can differ by a wide margin. Prefer realized_pnl for the banked figure.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pnl: Option<f64>,
/// Native realized P&L plus credited maker and taker rebates in USD, with fees already included. The exact numeric value is truncated toward zero to cents before JSON conversion. Omitted when the wallet has no native snapshot or the snapshot has no net realized value; never filled from another P&L basis.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub realized_pnl: Option<f64>,
/// The 0xinsider correction to Polymarket's P&L for neg-risk conversions. Present only when one is published; realized_pnl keeps Polymarket's own figure and pnl_correction.realized adds the amount to it. The grade and the ranking use the corrected figure. net_total is omitted here.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pnl_correction: Option<PnlCorrection>,
/// Full-history both-sides USD cash volume from a verified Polymarket user-volume observation. Omitted without coverage.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub volume: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub markets_traded: Option<i64>,
/// The wallet's win rate across ALL categories, not the filtered one. ?category= decides WHICH wallets are listed (the wallet must be ranked in that category); it does not rescope this field, so a soccer-filtered list still reports each wallet's overall rate. For a per-category record use GET /api/v1/trader/{address}/categories.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub win_rate: Option<f64>,
/// Observed trading style identifier. New rows use two_sided, category_focused, high_activity, diversified, mixed, or unclassified, counted over the wallet's sports and esports markets only. Historical identifiers remain readable during normal reclassification; style does not predict skill or intent.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub strategy_type: Option<String>,
/// Provider platform. Always polymarket.
pub platform: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_active: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListGamesResponse {
pub object: String,
pub data: Vec<Game>,
pub has_more: bool,
/// Pass as cursor for the next page. Present only when has_more is true.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// When this read assembled the catalog. Per-source vintage is on each game's freshness.
pub as_of: String,
pub coverage: GamesCoverage,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListInsiderRadarResponse {
pub object: String,
pub data: Vec<SuspiciousTrade>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListLargePositionsResponse {
pub object: String,
pub data: Vec<LargePosition>,
pub has_more: bool,
/// Opaque cursor for the next page; absent on the last page.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total ranked rows when the read model exposes a count; may be absent.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListLargeTradeCounterpartyExecutionsResponse {
pub object: String,
pub data: CounterpartyAnalysis,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListLargeTradeCounterpartyMakersResponse {
pub object: String,
pub data: CounterpartyMakerPage,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListLargeTradeHistoryResponse {
pub object: String,
pub data: Vec<LargeTrade>,
/// Writer-backed age and coverage for this page; it is part of the representation and the ETag, while transport cache facts remain in meta.
pub data_quality: DataQuality,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: LargeTradeHistoryMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListLargeTradesResponse {
pub object: String,
pub data: Vec<LargeTrade>,
/// Writer-backed age and coverage for this page; it is part of the representation and the ETag, while transport cache facts remain in meta.
pub data_quality: DataQuality,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; absent (or null) when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListLeaderboardResponse {
pub object: String,
pub data: Vec<LeaderboardEntry>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListPositionsResponse {
pub object: String,
pub data: Vec<Position>,
/// Writer-backed age and coverage for this page; it is part of the representation and the ETag, while transport cache facts remain in meta.
pub data_quality: DataQuality,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
/// Present only with consistency=snapshot. Dates the frozen response rows, not the provider's underlying observations.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub snapshot: Option<ListPositionsResponseSnapshot>,
pub meta: ResponseMeta,
}
/// Present only with consistency=snapshot. Dates the frozen response rows, not the provider's underlying observations.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListPositionsResponseSnapshot {
pub as_of: String,
pub expires_at: String,
pub row_count: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListSharpMoneyFlowsResponse {
pub object: String,
pub data: Vec<SmartMoneyFlowMarket>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListSmartMoneyFlowsResponse {
pub object: String,
pub data: Vec<SmartMoneyFlowMarket>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListSuspiciousTradesResponse {
pub object: String,
pub data: Vec<SuspiciousTrade>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListTrendingWalletsResponse {
pub object: String,
pub data: Vec<TrendingWallet>,
pub has_more: bool,
/// Opaque cursor for the next page; absent on the last page.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total ranked rows when the read model exposes a count; may be absent.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ListTrendingWalletsWindow {
/// `7d`
#[serde(rename = "7d")]
V7d,
/// `30d`
#[serde(rename = "30d")]
V30d,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ListTrendingWalletsWindow {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::V7d => "7d",
Self::V30d => "30d",
Self::Other(value) => value,
}
}
}
impl From<&str> for ListTrendingWalletsWindow {
fn from(value: &str) -> Self {
match value {
"7d" => Self::V7d,
"30d" => Self::V30d,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ListTrendingWalletsWindow {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ListTrendingWalletsWindow {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListWebhookDeliveriesResponse {
pub object: String,
pub data: Vec<WebhookDelivery>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListWebhookEventsResponse {
pub object: String,
pub data: Vec<WebhookEventDescriptor>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListWebhooksResponse {
pub object: String,
pub data: Vec<WebhookEndpoint>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListWhaleTradeCounterpartyExecutionsResponse {
pub object: String,
pub data: CounterpartyAnalysis,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListWhaleTradeCounterpartyMakersResponse {
pub object: String,
pub data: CounterpartyMakerPage,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListWhaleTradeHistoryResponse {
pub object: String,
pub data: Vec<LargeTrade>,
/// Writer-backed age and coverage for this page; it is part of the representation and the ETag, while transport cache facts remain in meta.
pub data_quality: DataQuality,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; absent (or null) when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: LargeTradeHistoryMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListWhaleTradesResponse {
pub object: String,
pub data: Vec<LargeTrade>,
/// Writer-backed age and coverage for this page; it is part of the representation and the ETag, while transport cache facts remain in meta.
pub data_quality: DataQuality,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
/// One side's score line inside a `live_sports_updated` pulse.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LiveSportsScoreEntry {
/// Team name, ordered for the league (away first where the league reads that way).
pub team: String,
/// This side's score as the provider sends it.
pub score: String,
/// Esports series only: this side's current-map score. Absent on other sports.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub map_score: Option<String>,
}
/// `live_sports_updated` data: one bounded pulse for one live game. A pulse fires when a material field moved since this game's previous delivered pulse (`scores`, `status`, `period`, `live`, `ended`), at most once per game per 20 seconds. A `live` or `ended` transition is exempt from that interval, and the game clock alone never fires a pulse. Suppressed frames are folded into the next pulse's `changed`, and its body carries the current state.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LiveSportsUpdatedData {
/// The Polymarket event slug this game trades under.
pub event_slug: String,
/// Provider game id, or `null` when the frame carried none.
#[serde(default)]
pub game_id: Option<String>,
/// League abbreviation, or `null`.
#[serde(default)]
pub league: Option<String>,
/// Durable per-game pulse ordinal, increasing by one per delivered pulse. Deliveries are not ordered, so drop a pulse whose version you already saw.
pub version: i64,
/// Material fields that moved since this game's previous delivered pulse.
pub changed: Vec<Changed>,
/// Provider frame time, or `null` when the frame carried none. Never replaced with a local clock.
#[serde(default)]
pub observed_at: Option<String>,
/// When the pulse was admitted.
pub published_at: String,
/// Provider status text, or `null`.
#[serde(default)]
pub status: Option<String>,
/// Provider period text ("Q3", "Map 2/3"), or `null`.
#[serde(default)]
pub period: Option<String>,
/// Game clock, or `null`. Not a material field: it never fires a pulse on its own.
#[serde(default)]
pub clock: Option<String>,
/// Provider live flag, or `null` when the frame carried none.
#[serde(default)]
pub live: Option<bool>,
/// Whether the provider has marked this game final.
pub ended: bool,
/// Per-side scores, one line per team.
pub scores: Vec<LiveSportsScoreEntry>,
/// Esports `BoN` series token, or `null` on every other sport.
#[serde(default)]
pub series_format: Option<String>,
/// Public game page. Re-read it after a gap instead of replaying pulses.
pub snapshot_url: String,
}
/// Provider-first bucketed OHLC price candles for a market's outcome tokens, derived from our stored copy of the Polymarket CLOB prices-history series (covers open and resolved markets).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketCandles {
/// Market condition id the candles were read for.
pub condition_id: String,
/// Bucketing granularity that produced these candles.
pub resolution: Resolution,
/// One entry per present provider token (YES first, then NO); empty when no tokens have been fetched yet.
pub outcomes: Vec<OutcomeCandles>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketFlow {
pub market: MarketFlowMarket,
/// Outcome-aware flow from all tracked whale trades in the window, without a grade filter. BUY YES and SELL NO add net exposure; BUY NO and SELL YES subtract it. Gross buy/sell volumes count both outcomes. Top positions are separately graded. Direction uses unrounded net: negative is NO, otherwise YES; the zero tie-break is not conviction. Canonical; smart_money is a deprecated byte-identical alias.
pub sharp_money: MarketFlowSharpMoney,
/// Deprecated alias of sharp_money; byte-identical and retained for backward compatibility.
pub smart_money: MarketFlowSmartMoney,
pub timeframe: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketFlowMarket {
pub id: String,
pub condition_id: String,
pub title: String,
#[serde(default)]
pub slug: Option<String>,
#[serde(default)]
pub category: Option<String>,
/// Provider platform. Always polymarket, or null when the row carries no stored value.
#[serde(default)]
pub platform: Option<String>,
}
/// Outcome-aware flow from all tracked whale trades in the window, without a grade filter. BUY YES and SELL NO add net exposure; BUY NO and SELL YES subtract it. Gross buy/sell volumes count both outcomes. Top positions are separately graded. Direction uses unrounded net: negative is NO, otherwise YES; the zero tie-break is not conviction. Canonical; smart_money is a deprecated byte-identical alias.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketFlowSharpMoney {
pub net_flow_usd: f64,
pub direction: Direction,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the net-flow direction outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Canonical key since #16304 (Polymarket's noun is large trade); whale_trade_count is its deprecated spelling, emitted beside it with the same value.
pub large_trade_count: i64,
pub whale_trade_count: i64,
pub buy_volume_usd: f64,
pub sell_volume_usd: f64,
pub top_positions: Vec<MarketFlowSharpMoneyTopPositionsItem>,
/// Age of the oldest stored position snapshot behind top_positions: the minimum wallet sync clock over the positions this body publishes. The roster is a stored read rather than a live one, and graded wallets sit on different sync tiers, so this names how current the whole roster is; the response is no fresher than this timestamp. Null when the roster is empty or no published position carries a sync clock. It does not date net_flow_usd, the volumes or the trade counts, which come from large-trade event times.
#[serde(default)]
pub oldest_snapshot_as_of: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketFlowSharpMoneyTopPositionsItem {
pub id: String,
pub address: String,
#[serde(default)]
pub username: Option<String>,
#[serde(default)]
pub grade: Option<String>,
pub side: Direction,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for this outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
pub size_usd: f64,
}
/// Deprecated alias of sharp_money; byte-identical and retained for backward compatibility.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketFlowSmartMoney {
pub net_flow_usd: f64,
pub direction: Direction,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the net-flow direction outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Canonical key since #16304 (Polymarket's noun is large trade); whale_trade_count is its deprecated spelling, emitted beside it with the same value.
pub large_trade_count: i64,
pub whale_trade_count: i64,
pub buy_volume_usd: f64,
pub sell_volume_usd: f64,
pub top_positions: Vec<MarketFlowSmartMoneyTopPositionsItem>,
/// Age of the oldest stored position snapshot behind top_positions: the minimum wallet sync clock over the positions this body publishes. The roster is a stored read rather than a live one, and graded wallets sit on different sync tiers, so this names how current the whole roster is; the response is no fresher than this timestamp. Null when the roster is empty or no published position carries a sync clock. It does not date net_flow_usd, the volumes or the trade counts, which come from large-trade event times.
#[serde(default)]
pub oldest_snapshot_as_of: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketFlowSmartMoneyTopPositionsItem {
pub id: String,
pub address: String,
#[serde(default)]
pub username: Option<String>,
#[serde(default)]
pub grade: Option<String>,
pub side: Direction,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for this outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
pub size_usd: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketHolder {
/// Composite `pos_<wallet>:<condition_id>:<outcome_index>`, the same id `GET /api/v1/positions` gives this leg.
pub id: String,
/// `trd_`-prefixed trader id, accepted by `GET /api/v1/trader/{address}`.
pub trader_id: String,
/// Lowercased proxy wallet.
pub address: String,
/// Polymarket's public display name from the same holder snapshot; null for a private wallet, which the provider redacts at the source.
#[serde(default)]
pub name: Option<String>,
/// The outcome this wallet nets to. A wallet holding both outcomes is listed once, on its net side; see `other_outcome_shares`.
pub side: Direction,
/// Polymarket CLOB token id (ERC1155 asset id, decimal string) for `side`; null when the market has no stored token id.
#[serde(default)]
pub token_id: Option<String>,
/// All-time trader grade. This route lists the S/A/B cohort only, the same cohort a Pick of the Day roster lists.
#[serde(default)]
pub grade: Option<MarketHolderGrade>,
/// Shares held on `side`, from the complete provider scan.
pub shares: f64,
/// Polymarket's own currentValue for this leg, in USD. Null when the snapshot predates the value map.
#[serde(default)]
pub current_value_usd: Option<f64>,
/// Shares on the other outcome when the wallet holds both. Absent when the wallet is one-sided.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub other_outcome_shares: Option<f64>,
/// The wallet's last recorded trade anywhere, null when unknown.
#[serde(default)]
pub last_traded_at: Option<String>,
/// This wallet's win rate in the market's canonical category (`market.category`): the share of its resolved markets in that category whose realized P&L closed positive, as a 0..1 fraction. Present only with `category_win_rate_status` = `measured`. The same read `GET /api/v1/trader/{address}/categories` serves per wallet.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_win_rate: Option<f64>,
/// The two counts `category_win_rate` is the ratio of, from the same row: `wins / decided` equals the rate. Every resolved Polymarket market the wallet traded in the market's canonical category, at any position size, rebuilt daily. Present only with `category_win_rate_status` = `measured`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_win_record: Option<MarketHolderCategoryWinRecord>,
/// The wallet's average entry price in the market's canonical category, as a 0..1 fraction of $1 (0.98 is 98 cents). Its sample is the wallet's settled markets in that category of $20 or more where it bought one side at a known price, so it can differ from the `category_win_record` sample. A wallet that buys near-certain outcomes wins almost every time, so read the win rate beside this price. Present only with `category_win_rate_status` = `measured`, and only when that sample has at least 5 markets. When `category_win_rate_game` is present the price is still the `Esports` category's: there is no per-game price. Absent otherwise and on payloads predating the field.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_avg_price: Option<f64>,
/// For an esports market, the game `category_win_rate` and `category_win_record` were measured in: `LoL`, `CS2`, `Dota 2`, `Valorant`, `Call of Duty`, `Honor of Kings`, `Mobile Legends: Bang Bang`, `Overwatch`, `Rainbow Six Siege`, `Rocket League` or `StarCraft II`. Present only when the wallet's record in that game clears the 5-resolved-market floor, in which case the rate and record are the game's rather than the `Esports` bucket's. Absent when the rate is the bucket's (the wallet's game history is under the floor), on every non-esports market, and beside every non-measured status. Label the rate with this when present and with the market's `category` otherwise.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_win_rate_game: Option<String>,
/// Why `category_win_rate` is present or absent: `measured` (rate present), `not_enough_data` (under the floor of 5 decided markets in the category), or `unavailable` (the market has no canonical category, or the read failed; retry later). Always present on this route.
pub category_win_rate_status: CategoryWinRateStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_evidence: Option<HolderCategoryEvidence>,
/// Days since this wallet's first trade. The five badge fields are present together, only for a wallet that draws at least one badge.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub wallet_age_days: Option<f64>,
/// True when the wallet's first trade was under 30 days ago.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_new_wallet: Option<bool>,
/// Distinct markets the wallet has traded.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub markets_traded: Option<i64>,
/// True when the wallet is flagged as automated.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_bot: Option<bool>,
/// The wallet's X handle without the @, when linked.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub x_username: Option<String>,
}
/// The two counts `category_win_rate` is the ratio of, from the same row: `wins / decided` equals the rate. Every resolved Polymarket market the wallet traded in the market's canonical category, at any position size, rebuilt daily. Present only with `category_win_rate_status` = `measured`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketHolderCategoryWinRecord {
/// Resolved markets in the category that this wallet closed with a profit.
pub wins: i64,
/// Resolved markets in the category that this wallet closed with a profit or a loss. A market resolved at zero realized P&L is in neither count.
pub decided: i64,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MarketHolderGrade {
/// `S`
#[serde(rename = "S")]
S,
/// `A`
#[serde(rename = "A")]
A,
/// `B`
#[serde(rename = "B")]
B,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl MarketHolderGrade {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::S => "S",
Self::A => "A",
Self::B => "B",
Self::Other(value) => value,
}
}
}
impl From<&str> for MarketHolderGrade {
fn from(value: &str) -> Self {
match value {
"S" => Self::S,
"A" => Self::A,
"B" => Self::B,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for MarketHolderGrade {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for MarketHolderGrade {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketHoldersMarket {
/// `mkt_`-prefixed market id.
pub id: String,
pub condition_id: String,
#[serde(default)]
pub title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_slug: Option<String>,
/// The canonical category bucket `category_win_rate` is measured in.
#[serde(default)]
pub category: Option<String>,
#[serde(default)]
pub outcome_yes: Option<String>,
#[serde(default)]
pub outcome_no: Option<String>,
#[serde(default)]
pub token_id_yes: Option<String>,
#[serde(default)]
pub token_id_no: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
/// A settled market's roster is whatever the provider still lists as open; it empties as holders redeem.
pub status: MarketHoldersMarketStatus,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MarketHoldersMarketStatus {
/// `open`
#[serde(rename = "open")]
Open,
/// `closed`
#[serde(rename = "closed")]
Closed,
/// `resolved`
#[serde(rename = "resolved")]
Resolved,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl MarketHoldersMarketStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Open => "open",
Self::Closed => "closed",
Self::Resolved => "resolved",
Self::Other(value) => value,
}
}
}
impl From<&str> for MarketHoldersMarketStatus {
fn from(value: &str) -> Self {
match value {
"open" => Self::Open,
"closed" => Self::Closed,
"resolved" => Self::Resolved,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for MarketHoldersMarketStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for MarketHoldersMarketStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketHoldersScan {
/// `cached`: the shared holder snapshot, at most 180 s old at compute time. `live`: the provider answered a fresh complete walk for this compute.
pub source: ProviderReadSource,
/// Always true on a served roster. An incomplete or unstable scan is a 503, never a partial list.
pub complete: bool,
/// When the provider walk that produced the holder population finished.
#[serde(default)]
pub fetched_at: Option<String>,
/// Every distinct wallet the complete walk saw with open shares, graded or not. The roster lists the graded S/A/B subset.
pub wallet_count: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketHoldersSideGrades {
pub s: i64,
pub a: i64,
pub b: i64,
}
/// Roster totals BEFORE any `outcome` or `min_grade` filter, so a page always knows the whole market it was cut from.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketHoldersTotals {
/// Distinct graded wallets with net exposure; a two-sided wallet counts once.
pub graded_count: i64,
pub yes_count: i64,
pub no_count: i64,
pub yes_shares: f64,
pub no_shares: f64,
/// Polymarket currentValue summed over the graded wallets netting YES.
pub yes_usd: f64,
/// Polymarket currentValue summed over the graded wallets netting NO.
pub no_usd: f64,
pub yes_grades: MarketHoldersSideGrades,
pub no_grades: MarketHoldersSideGrades,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MarketScope {
/// `all_markets`
#[serde(rename = "all_markets")]
AllMarkets,
/// `sports_and_esports`
#[serde(rename = "sports_and_esports")]
SportsAndEsports,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl MarketScope {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::AllMarkets => "all_markets",
Self::SportsAndEsports => "sports_and_esports",
Self::Other(value) => value,
}
}
}
impl From<&str> for MarketScope {
fn from(value: &str) -> Self {
match value {
"all_markets" => Self::AllMarkets,
"sports_and_esports" => Self::SportsAndEsports,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for MarketScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for MarketScope {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSearchResult {
pub id: String,
pub condition_id: String,
pub title: String,
#[serde(default)]
pub slug: Option<String>,
#[serde(default)]
pub category: Option<String>,
/// Provider platform. Always polymarket, or null when the row carries no stored value.
#[serde(default)]
pub platform: Option<String>,
/// closed once Polymarket has closed trading or the market has resolved; active otherwise. The same rule labels a market on markets/search, markets/explore and market/{condition_id}/snapshot.
pub status: MarketSearchResultStatus,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MarketSearchResultStatus {
/// `active`
#[serde(rename = "active")]
Active,
/// `closed`
#[serde(rename = "closed")]
Closed,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl MarketSearchResultStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Active => "active",
Self::Closed => "closed",
Self::Other(value) => value,
}
}
}
impl From<&str> for MarketSearchResultStatus {
fn from(value: &str) -> Self {
match value {
"active" => Self::Active,
"closed" => Self::Closed,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for MarketSearchResultStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for MarketSearchResultStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshot {
pub market: MarketSnapshotMarket,
pub outcomes: Vec<MarketSnapshotOutcomesItem>,
pub liquidity: MarketSnapshotLiquidity,
pub sports: MarketSnapshotSports,
pub freshness: MarketSnapshotFreshnessBySource,
/// Price and spread trust metadata. Present only when expand=trust or expand[]=trust is requested.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trust: Option<MarketSnapshotTrust>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotFreshness {
pub status: MarketSnapshotFreshnessStatus,
pub source: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub as_of: Option<String>,
/// Age-relative freshness bound when known. Omitted for live_sports: its as_of records compute start, while its two-second freshness marker starts at publication.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stale_after_s: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotFreshnessBySource {
pub market_data: MarketSnapshotFreshness,
pub top_of_book: MarketSnapshotFreshness,
/// Freshness requires the dedicated two-second post-publication marker to match the retained vintage and non-invalidated data timestamp; timestamp presence alone is insufficient. as_of records compute start, so stale_after_s is omitted rather than implying as_of plus two seconds. Missing membership is not_live only in a fresh source; stale membership remains stale and unreadable source data is unavailable. Repeated lookups reuse the shared payload and typed index for an unchanged generation.
pub live_sports: MarketSnapshotFreshness,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MarketSnapshotFreshnessStatus {
/// `fresh`
#[serde(rename = "fresh")]
Fresh,
/// `stale`
#[serde(rename = "stale")]
Stale,
/// `available`
#[serde(rename = "available")]
Available,
/// `not_live`
#[serde(rename = "not_live")]
NotLive,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl MarketSnapshotFreshnessStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Fresh => "fresh",
Self::Stale => "stale",
Self::Available => "available",
Self::NotLive => "not_live",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for MarketSnapshotFreshnessStatus {
fn from(value: &str) -> Self {
match value {
"fresh" => Self::Fresh,
"stale" => Self::Stale,
"available" => Self::Available,
"not_live" => Self::NotLive,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for MarketSnapshotFreshnessStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for MarketSnapshotFreshnessStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotLiquidity {
pub source: String,
/// Polymarket's traded-volume figure for the market, as last stored: cumulative taker volume in shares, each counted at its $1 settlement value, not the cash that changed hands (100 shares bought at $0.40 add 100, not 40). It can lag Polymarket's current figure. The name ends in _usd because Polymarket prints this figure with a dollar sign; it is a share count. Null when unavailable.
#[serde(default)]
pub volume_usd: Option<f64>,
#[serde(default)]
pub liquidity_usd: Option<f64>,
/// Polymarket's taker volume over the 24 hours before volume_24h_as_of, in shares, each counted at its $1 settlement value, not the cash that changed hands (100 shares bought at $0.40 add 100, not 40). The name ends in _usd because Polymarket prints volume with a dollar sign; it is a share count. Null when Polymarket does not report one: it omits the figure on closed markets and on open markets with no trades in the last 24 hours.
#[serde(default)]
pub volume_24h_usd: Option<f64>,
/// When 0xinsider read volume_24h_usd from Polymarket; the figure covers the 24 hours before this time. Null exactly when volume_24h_usd is null.
#[serde(default)]
pub volume_24h_as_of: Option<String>,
/// Always null. Polymarket snapshot prices come from outcome top-of-book midpoints.
#[serde(default)]
pub last_price: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotMarket {
pub id: String,
pub condition_id: String,
pub provider: String,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub slug: Option<String>,
#[serde(default)]
pub page_slug: Option<String>,
#[serde(default)]
pub event_slug: Option<String>,
#[serde(default)]
pub category: Option<String>,
/// closed once Polymarket has closed trading or the market has resolved; active otherwise. The same rule labels a market on markets/search, markets/explore and market/{condition_id}/snapshot.
pub status: MarketSearchResultStatus,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub image: Option<String>,
/// Polymarket series slug from the canonical identity row, null when unavailable. A sports series identifies a league and must not be used as a per-matchup grouping key.
#[serde(default)]
pub series_slug: Option<String>,
/// Always null. Deprecated (#24169): 0xinsider has never stored a value for this field. The key stays on the wire so existing clients keep parsing; do not read it.
#[serde(default)]
pub market_type: Option<String>,
/// Always null. Deprecated (#24169): 0xinsider stores no value for this field on a Polymarket market. The key stays on the wire so existing clients keep parsing; do not read it. market.status and market.resolved_at say whether the market has closed or resolved.
#[serde(default)]
pub market_result: Option<String>,
#[serde(default)]
pub created_at: Option<String>,
#[serde(default)]
pub end_date: Option<String>,
#[serde(default)]
pub resolved_at: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotOutcomesItem {
pub side: Side,
pub label: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub token_id: Option<String>,
/// The outcome's display price, the probability Polymarket shows for it: the midpoint of the cached top of book when the spread is at most 0.10, otherwise the token's last trade price. Omitted when neither is known, including a book wider than 0.10 with no last trade on record; top_of_book still carries that book's best_bid and best_ask. expand=trust explains a missing value in trust.current_price.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub current_price: Option<f64>,
pub top_of_book: MarketSnapshotTopOfBook,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotSports {
pub status: MarketSnapshotSportsStatus,
pub source: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub live_match_key: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub live_league_key: Option<String>,
/// Live-score period and elapsed may be omitted when unavailable (older responses use null). A score entry may omit full_name when it equals team; fall back to team. Distinct aliases and score admission markers are preserved. For live esports, scores[].map_score retains an available pair through an update omitting both sides only within the same live map, series format and series totals. Map changes and final state follow current provider updates; absent detail remains unavailable. Optional tennis_points supplies independently sourced current-game points and serving side; absence means unavailable. Compatible unexpired tennis_points survives temporary supplemental snapshot unavailability or contention; expiry and set/game mismatch still clear it.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub live_score: Option<MarketSnapshotSportsLiveScore>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
/// Live-score period and elapsed may be omitted when unavailable (older responses use null). A score entry may omit full_name when it equals team; fall back to team. Distinct aliases and score admission markers are preserved. For live esports, scores[].map_score retains an available pair through an update omitting both sides only within the same live map, series format and series totals. Map changes and final state follow current provider updates; absent detail remains unavailable. Optional tennis_points supplies independently sourced current-game points and serving side; absence means unavailable. Compatible unexpired tennis_points survives temporary supplemental snapshot unavailability or contention; expiry and set/game mismatch still clear it.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotSportsLiveScore {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tennis_points: Option<TennisPoints>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MarketSnapshotSportsStatus {
/// `fresh`
#[serde(rename = "fresh")]
Fresh,
/// `stale`
#[serde(rename = "stale")]
Stale,
/// `not_live`
#[serde(rename = "not_live")]
NotLive,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl MarketSnapshotSportsStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Fresh => "fresh",
Self::Stale => "stale",
Self::NotLive => "not_live",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for MarketSnapshotSportsStatus {
fn from(value: &str) -> Self {
match value {
"fresh" => Self::Fresh,
"stale" => Self::Stale,
"not_live" => Self::NotLive,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for MarketSnapshotSportsStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for MarketSnapshotSportsStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotTopOfBook {
pub status: EnrichmentStatus,
pub source: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub best_bid: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub best_ask: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub spread_bps: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bid_depth_usdc: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ask_depth_usdc: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
/// Price and spread trust metadata returned only when GET /api/v1/market/{condition_id}/snapshot includes expand=trust.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketSnapshotTrust {
pub current_price: TrustMetadata,
pub spread_bps: TrustMetadata,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MatchStatus {
/// `scheduled`
#[serde(rename = "scheduled")]
Scheduled,
/// `in_progress`
#[serde(rename = "in_progress")]
InProgress,
/// `halftime`
#[serde(rename = "halftime")]
Halftime,
/// `penalty_shootout`
#[serde(rename = "penalty_shootout")]
PenaltyShootout,
/// `delayed`
#[serde(rename = "delayed")]
Delayed,
/// `suspended`
#[serde(rename = "suspended")]
Suspended,
/// `final`
#[serde(rename = "final")]
Final,
/// `final_overtime`
#[serde(rename = "final_overtime")]
FinalOvertime,
/// `final_shootout`
#[serde(rename = "final_shootout")]
FinalShootout,
/// `awarded`
#[serde(rename = "awarded")]
Awarded,
/// `forfeit`
#[serde(rename = "forfeit")]
Forfeit,
/// `not_necessary`
#[serde(rename = "not_necessary")]
NotNecessary,
/// `postponed`
#[serde(rename = "postponed")]
Postponed,
/// `cancelled`
#[serde(rename = "cancelled")]
Cancelled,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl MatchStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Scheduled => "scheduled",
Self::InProgress => "in_progress",
Self::Halftime => "halftime",
Self::PenaltyShootout => "penalty_shootout",
Self::Delayed => "delayed",
Self::Suspended => "suspended",
Self::Final => "final",
Self::FinalOvertime => "final_overtime",
Self::FinalShootout => "final_shootout",
Self::Awarded => "awarded",
Self::Forfeit => "forfeit",
Self::NotNecessary => "not_necessary",
Self::Postponed => "postponed",
Self::Cancelled => "cancelled",
Self::Unknown => "unknown",
Self::Other(value) => value,
}
}
}
impl From<&str> for MatchStatus {
fn from(value: &str) -> Self {
match value {
"scheduled" => Self::Scheduled,
"in_progress" => Self::InProgress,
"halftime" => Self::Halftime,
"penalty_shootout" => Self::PenaltyShootout,
"delayed" => Self::Delayed,
"suspended" => Self::Suspended,
"final" => Self::Final,
"final_overtime" => Self::FinalOvertime,
"final_shootout" => Self::FinalShootout,
"awarded" => Self::Awarded,
"forfeit" => Self::Forfeit,
"not_necessary" => Self::NotNecessary,
"postponed" => Self::Postponed,
"cancelled" => Self::Cancelled,
"unknown" => Self::Unknown,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for MatchStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for MatchStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MatchType {
/// `COMPLEMENTARY`
#[serde(rename = "COMPLEMENTARY")]
Complementary,
/// `MINT`
#[serde(rename = "MINT")]
Mint,
/// `MERGE`
#[serde(rename = "MERGE")]
Merge,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl MatchType {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Complementary => "COMPLEMENTARY",
Self::Mint => "MINT",
Self::Merge => "MERGE",
Self::Other(value) => value,
}
}
}
impl From<&str> for MatchType {
fn from(value: &str) -> Self {
match value {
"COMPLEMENTARY" => Self::Complementary,
"MINT" => Self::Mint,
"MERGE" => Self::Merge,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for MatchType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for MatchType {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct McpJsonRpcError {
pub jsonrpc: String,
#[serde(default)]
pub id: Option<serde_json::Value>,
pub error: McpJsonRpcErrorError,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct McpJsonRpcErrorError {
pub code: i64,
pub message: String,
/// Optional JSON-serializable error data.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum McpMethod {
/// `initialize`
#[serde(rename = "initialize")]
Initialize,
/// `notifications/initialized`
#[serde(rename = "notifications/initialized")]
NotificationsInitialized,
/// `notifications/cancelled`
#[serde(rename = "notifications/cancelled")]
NotificationsCancelled,
/// `ping`
#[serde(rename = "ping")]
Ping,
/// `tools/list`
#[serde(rename = "tools/list")]
ToolsList,
/// `tools/call`
#[serde(rename = "tools/call")]
ToolsCall,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl McpMethod {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Initialize => "initialize",
Self::NotificationsInitialized => "notifications/initialized",
Self::NotificationsCancelled => "notifications/cancelled",
Self::Ping => "ping",
Self::ToolsList => "tools/list",
Self::ToolsCall => "tools/call",
Self::Other(value) => value,
}
}
}
impl From<&str> for McpMethod {
fn from(value: &str) -> Self {
match value {
"initialize" => Self::Initialize,
"notifications/initialized" => Self::NotificationsInitialized,
"notifications/cancelled" => Self::NotificationsCancelled,
"ping" => Self::Ping,
"tools/list" => Self::ToolsList,
"tools/call" => Self::ToolsCall,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for McpMethod {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for McpMethod {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Mode {
/// `live`
#[serde(rename = "live")]
Live,
/// `stable`
#[serde(rename = "stable")]
Stable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Mode {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Live => "live",
Self::Stable => "stable",
Self::Other(value) => value,
}
}
}
impl From<&str> for Mode {
fn from(value: &str) -> Self {
match value {
"live" => Self::Live,
"stable" => Self::Stable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Mode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Mode {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum NextAction {
/// `poll`
#[serde(rename = "poll")]
Poll,
/// `download`
#[serde(rename = "download")]
Download,
/// `resubmit`
#[serde(rename = "resubmit")]
Resubmit,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl NextAction {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Poll => "poll",
Self::Download => "download",
Self::Resubmit => "resubmit",
Self::Other(value) => value,
}
}
}
impl From<&str> for NextAction {
fn from(value: &str) -> Self {
match value {
"poll" => Self::Poll,
"download" => Self::Download,
"resubmit" => Self::Resubmit,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for NextAction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for NextAction {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ObservationSource {
/// `board_snapshot`
#[serde(rename = "board_snapshot")]
BoardSnapshot,
/// `clob_display`
#[serde(rename = "clob_display")]
ClobDisplay,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ObservationSource {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::BoardSnapshot => "board_snapshot",
Self::ClobDisplay => "clob_display",
Self::Other(value) => value,
}
}
}
impl From<&str> for ObservationSource {
fn from(value: &str) -> Self {
match value {
"board_snapshot" => Self::BoardSnapshot,
"clob_display" => Self::ClobDisplay,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ObservationSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ObservationSource {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Outcome {
/// `pending`
#[serde(rename = "pending")]
Pending,
/// `win`
#[serde(rename = "win")]
Win,
/// `loss`
#[serde(rename = "loss")]
Loss,
/// `void`
#[serde(rename = "void")]
Void,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Outcome {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Pending => "pending",
Self::Win => "win",
Self::Loss => "loss",
Self::Void => "void",
Self::Other(value) => value,
}
}
}
impl From<&str> for Outcome {
fn from(value: &str) -> Self {
match value {
"pending" => Self::Pending,
"win" => Self::Win,
"loss" => Self::Loss,
"void" => Self::Void,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Outcome {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Outcome {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// One outcome token's bucketed candle series.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct OutcomeCandles {
/// Provider CLOB token id the candles were read for.
pub token_id: String,
/// Canonical side label.
pub outcome: Direction,
/// OHLC candles for this outcome token, ascending by bucket start.
pub candles: Vec<Candle>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum PaidDataAccess {
/// `active`
#[serde(rename = "active")]
Active,
/// `lapsed`
#[serde(rename = "lapsed")]
Lapsed,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl PaidDataAccess {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Active => "active",
Self::Lapsed => "lapsed",
Self::Other(value) => value,
}
}
}
impl From<&str> for PaidDataAccess {
fn from(value: &str) -> Self {
match value {
"active" => Self::Active,
"lapsed" => Self::Lapsed,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for PaidDataAccess {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for PaidDataAccess {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickHolder {
pub address: String,
#[serde(default)]
pub name: Option<String>,
/// All-time trader grade (S, A, B, C, D, F).
#[serde(default)]
pub grade: Option<String>,
/// The wallet's most recent recorded trade time, stamped at serve time from its current trader record rather than frozen with the pick. Always sent; null when no trade time is recorded.
#[serde(default)]
pub last_traded_at: Option<String>,
/// 0xinsider profile path segment this wallet links to: `@<username>` when that username resolves to this wallet alone, otherwise the lowercase wallet. Percent-encode the part after `@` and append to `https://0xinsider.com/profile/`. Stamped at serve time; absent on a body cached before the field shipped.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profile_segment: Option<String>,
pub shares: f64,
/// USD entry value of this wallet's position: `shares` times the pick's frozen `backed_price`. An entry valuation, never a live balance or the provider's current value. Omitted when the holder snapshot has no valid price.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entry_value_usd: Option<f64>,
/// This wallet's win rate in the pick's canonical category bucket (the pick's `category` field, e.g. Basketball -- label the rate with it, never with the narrower `display_category` league, except when `category_win_rate_game` is present, in which case the rate is that game's and is labelled with it): the share of the wallet's resolved markets in that category whose realized P&L closed positive, as a 0..1 fraction. Present only with `category_win_rate_status` = `measured`, on `display_holders` entries, and only when the wallet clears the resolved-market floor; recomputed at serve time from the current category read model, not frozen with the pick. Absent on `holders` entries, legacy rows, and payloads predating the field.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_win_rate: Option<f64>,
/// The two counts `category_win_rate` is the ratio of, read from the same row: `wins / decided` equals the rate. Counts every resolved Polymarket market the wallet traded in the pick's canonical category (or in its game, when `category_win_rate_game` is present), at any position size; the counts are rebuilt daily. Present only with `category_win_rate_status` = `measured`; absent otherwise and on payloads predating the field.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_win_record: Option<PickHolderCategoryWinRecord>,
/// The wallet's average entry price in the pick's canonical category, as a 0..1 fraction of $1 (0.98 is 98 cents). Its sample is the wallet's settled markets in that category of $20 or more where it bought one side at a known price, so it can differ from the `category_win_record` sample. A wallet that buys near-certain outcomes wins almost every time, so read the win rate beside this price. Present only with `category_win_rate_status` = `measured`, and only when that sample has at least 5 markets. When `category_win_rate_game` is present the price is still the `Esports` category's: there is no per-game price. Absent otherwise and on payloads predating the field.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_avg_price: Option<f64>,
/// For an esports pick, the game `category_win_rate` and `category_win_record` were measured in, by the same name the pick's `display_category` uses for it: `LoL`, `CS2`, `Dota 2`, `Valorant`, `Call of Duty`, `Honor of Kings`, `Mobile Legends: Bang Bang`, `Overwatch`, `Rainbow Six Siege`, `Rocket League` or `StarCraft II`. Present only when the wallet's record in that game clears the 5-resolved-market floor, in which case the rate and record are the game's rather than the `Esports` bucket's. Absent when the rate is the bucket's (the wallet's game history is under the floor), on every non-esports pick, beside every non-measured status, and on payloads predating the field. Label the rate with this when present and with `category` otherwise.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_win_rate_game: Option<String>,
/// Why `category_win_rate` is present or absent on a `display_holders` entry: `measured` (rate present), `not_enough_data` (the wallet is below the resolved-market floor of 5 in the category), or `unavailable` (the annotation read failed; retry later). Absent entirely on `holders` entries, legacy rows, and payloads predating the field -- absence means the roster was never annotated, not a small sample.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_win_rate_status: Option<CategoryWinRateStatus>,
/// Days since this wallet's first trade. Stamped at serve time from the wallet's current trader record, never frozen with the pick. The five badge fields are present together, and only for a wallet that carries at least one badge; all absent means no badge, or a body cached before the fields shipped.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub wallet_age_days: Option<f64>,
/// True when the wallet's first trade was under 30 days ago. Stamped at serve time from the wallet's current trader record, never frozen with the pick. The five badge fields are present together, and only for a wallet that carries at least one badge; all absent means no badge, or a body cached before the fields shipped.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_new_wallet: Option<bool>,
/// Distinct markets this wallet has traded. Stamped at serve time from the wallet's current trader record, never frozen with the pick. The five badge fields are present together, and only for a wallet that carries at least one badge; all absent means no badge, or a body cached before the fields shipped.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub markets_traded: Option<i64>,
/// True when the wallet has traded 10,000 or more distinct markets, the breadth floor 0xinsider uses to mark automated wallets. It is a breadth rule, not proof of automation. Stamped at serve time from the wallet's current trader record, never frozen with the pick. The five badge fields are present together, and only for a wallet that carries at least one badge; all absent means no badge, or a body cached before the fields shipped.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_bot: Option<bool>,
/// The wallet's X handle from its Polymarket profile, normalized to 1-15 characters of [A-Za-z0-9_] with no `@`. Link it as `https://x.com/<handle>`. Stamped at serve time from the wallet's current trader record, never frozen with the pick. The five badge fields are present together, and only for a wallet that carries at least one badge; all absent means no badge, or a body cached before the fields shipped.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub x_username: Option<String>,
}
/// The two counts `category_win_rate` is the ratio of, read from the same row: `wins / decided` equals the rate. Counts every resolved Polymarket market the wallet traded in the pick's canonical category (or in its game, when `category_win_rate_game` is present), at any position size; the counts are rebuilt daily. Present only with `category_win_rate_status` = `measured`; absent otherwise and on payloads predating the field.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickHolderCategoryWinRecord {
/// Resolved markets in the category that this wallet closed with a profit.
pub wins: i64,
/// Resolved markets in the category that this wallet closed with a profit or a loss. A market resolved at zero realized P&L is in neither count.
pub decided: i64,
}
/// Recorded lead wallet facts on a full response when available. Position and category history are frozen at publication; they are not live balances or pick win probabilities.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickLeadBacker {
/// Lead trader wallet address.
pub address: String,
/// Recorded provider display name.
#[serde(default)]
pub name: Option<String>,
/// Trader grade recorded at publication.
pub grade: String,
/// Canonical sport of the recorded directional history.
pub category: String,
/// Provider-reported position value on the backed outcome at publication, in USD. It is the gross value on that outcome and does not subtract shares the wallet held on the other outcome; see net_position_usd. It is not the entry cost or a live balance.
pub position_usd: f64,
/// Net value of the lead's position toward the backed outcome at publication, in USD: shares held on the backed outcome minus shares held on the other outcome of the same market, valued at the backed outcome's provider price at publication. Present only when the wallet also held the other outcome; absent for a one-sided position, whose net equals position_usd. It is not a live balance.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub net_position_usd: Option<f64>,
/// Start time of the provider position fetch used at publication. This conservative observation clock precedes completion; the position is not a live balance.
pub position_observed_at: String,
/// Distinct directional events in the recorded category history.
pub directional_event_count: i64,
/// Events with strictly positive native terminal P&L in the same frozen category sample as directional_event_count, realized_pnl_usd and roi. Multiple market positions in one event contribute one combined event result. Zero-profit events remain in directional_event_count but do not increase this count. This is a recorded wallet result, not a market win rate or the pick's win probability.
pub profitable_event_count: i64,
/// Realized P&L over the recorded directional category sample, in USD. Excludes open positions and later changes.
pub realized_pnl_usd: f64,
/// Recorded entry basis of that directional sample, in USD; not a claim of complete trading costs or fees.
pub entry_basis_usd: f64,
/// Realized P&L divided by recorded entry basis, as a fraction: 0.10 means 10%. This is a wallet statistic, not a pick win probability.
pub roi: f64,
/// Peak-to-trough realized P&L drawdown after each event result in the recorded sample, in USD. Excludes intragame, unrealized, and account equity drawdown.
pub max_realized_drawdown_usd: f64,
/// Timestamp when the directional history evidence was recorded, separate from the provider position snapshot clock.
pub recorded_at: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDay {
/// Full success containing entitled proof-readable picks.
pub state: String,
/// The pick's local publication date (YYYY-MM-DD).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_date: Option<String>,
/// Deprecated compatibility daily release slot; use pick_id for identity and publication_order for scheduling.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_rank: Option<i64>,
/// Published picks for this product day in the returned display order. Use pick_id for identity.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub picks: Option<Vec<PickOfTheDay>>,
/// Number of items in `picks`: the proof-readable picks. Picks held in `proof_pending_picks` are not counted.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_count: Option<i64>,
/// Rank-ordered entitled selections that have not released. Every row retains release_at and kickoff; unauthorized scheduled ranks appear only in identity-free locked_picks.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scheduled_picks: Option<Vec<ScheduledPickSlot>>,
/// Human-readable matchup (e.g. "Portugal vs. Uzbekistan").
#[serde(default, skip_serializing_if = "Option::is_none")]
pub matchup: Option<String>,
/// Recorded canonical sport category. Prefer display_category for the public competition label.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
/// Frozen public presentation category. Tennis uses the provider-verified ATP or WTA tour, including Wimbledon and doubles; unknown tours retain Tennis. Every men's and women's cricket competition uses Cricket. Other sports use the curated league, esports title or soccer competition label, then the provider series name without its season year, then category when no competition is known. The canonical category and measured cohort are unchanged: an esports title still uses the pooled Esports category. Optional for mixed-version client compatibility.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_category: Option<String>,
/// Provider platform. Always polymarket.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub platform: Option<String>,
/// The pick's stored release instant. Use the returned timestamp; published_at records when publication occurred.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub release_at: Option<String>,
/// True only before the pick's stored release instant (a pre-release embargo flag); effectively always false on a served, already-published pick. To detect that the backed game has kicked off, use `game_started`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_locked: Option<bool>,
/// True once the backed game's kickoff has passed (kickoff <= now). When true the snapshotted pre-game price is no longer actionable. Absent for a legacy pick with no stored kickoff (treat as not-started).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub game_started: Option<bool>,
/// Whether the backed game is over according to the cached live scoreboard, read at serve time. Tells a finished game from one still in play before `outcome` settles. Omitted when the pick has no event slug or no scoreboard is cached for it; absence is unknown, never false.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub game_ended: Option<bool>,
/// Settlement outcome of the backed side; 'pending' until the market resolves.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome: Option<Outcome>,
/// Pre-formatted SETTLEMENT STATUS for display: "Win" / "Loss" / "Void" / "Pending" -- the outcome enum above as a label. Convenience only; outcome is the source value. NOTE: this is the win/loss STATUS, not the backed side. The backed side is pick_outcome_label ("Belgium (-2.5)") -- a different field answering a different question.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_display: Option<String>,
/// The backed side phrased as a bet: a team for a moneyline (e.g. "Portugal"), the handicap line for a spread (e.g. "Belgium (-2.5)"), or "{team} to advance" for a knockout advancement market (e.g. "Spain to advance").
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_outcome_label: Option<String>,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the backed outcome; omitted when unavailable (e.g. unsynced markets).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub token_id: Option<String>,
/// The backed side phrased as a bet (e.g. "Portugal to win").
#[serde(default, skip_serializing_if = "Option::is_none")]
pub position: Option<String>,
/// One-line summary of which side sharp money is backing. Required on every item in `picks`: a current-day published pick whose required holder proof is not safely readable is listed in `proof_pending_picks` instead of being served with a partial success shape or a synthetic zero, and the route returns 503 read_model_warming only when no published pick has readable proof.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub side_summary: Option<String>,
/// S/A wallet count on the backed side in the public V1 compatibility projection.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sharp_wallet_count: Option<i64>,
/// Deprecated spelling of sharp_wallet_count with the same value.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub smart_wallet_count: Option<i64>,
/// Best recorded S/A grade in the public V1 compatibility projection.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_grade: Option<String>,
/// Recorded sharp-money magnitude in USD when available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sharp_usd: Option<f64>,
/// Deprecated spelling of sharp_usd with the same value.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub smart_usd: Option<f64>,
/// Frozen pre-game probability (0..1) for the backed side, written once at publication. It is the Polymarket CLOB order book midpoint at release, not an executed fill: a buyer lifts the ask, so a subscriber's own entry is usually a little worse than this price.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub backed_price: Option<f64>,
/// Full-only disclosure when backed_price was recovered from provider history within 30 seconds before publication. Render beside the price. Absent for ordinary publication captures and teasers; this is a historical reference, not an executed fill.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entry_price_note: Option<String>,
/// Pre-formatted backed_price as cents-on-the-dollar odds, to ONE decimal: "62.0c" / "99.9c". Never rounded to a whole cent -- a 99.9c favorite is not a 100c certainty. Convenience only; backed_price is the source value. Omitted when backed_price is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub odds_display: Option<String>,
/// The flat stake the published record puts on every pick, in USD: 1000 since 2026-09-22 (it was 100 before). Present exactly when return_usd is, so a reader never has to know the stake from anywhere else.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stake_usd: Option<f64>,
/// Gross return of stake_usd at the frozen midpoint price (stake_usd / backed_price). A real fill pays the ask, so an executed stake usually returns a little less. Omitted with backed_price.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub return_usd: Option<f64>,
/// The same return on a literal $100 (100 / backed_price), kept for compatibility: the field predates stake_usd and its name promises the $100 basis, so a client that scales it to its own stake stays right. Present exactly when return_usd is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub return_per_100: Option<f64>,
/// Pre-formatted return_usd as USD with cents and thousands separators: "$1,612.90" / "$12,500.00". The GROSS return (the stake included), so it carries no sign. Convenience only; return_usd is the source value. Omitted when return_usd is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payout_display: Option<String>,
/// Pre-formatted PROFIT on the stake -- return_usd minus stake_usd, i.e. the payout net of what you put in -- as a signed USD string: "+$612.90". Distinct from payout_display, which is gross. Omitted when return_usd is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profit_display: Option<String>,
/// Backend-owned CLV capture disposition. "pending" means no capture decision exists yet; terminal provider or quality statuses remain distinguishable. The raw close price and timestamp are never serialized.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_status: Option<String>,
/// Backend-owned CLV evidence basis. `frozen_displayed_entry` uses the persisted displayed entry. `historical_provider_entry` uses a known-CLOB point at or before publication. `historical_provider_price_match` requires the latest point in the prior hour to match. `historical_provider_nearby_price_match` requires a matching point within five minutes before publication. Source-null bases preserve unknown original provenance.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_basis: Option<String>,
/// Closing-line value toward the backed side, computed as (close / entry - 1) * 100. The basis-specific provider p entry must match the stored display; historical_provider_price_match also requires source provenance to remain null and its latest entry to be within the one-hour window at or before publication. Every basis requires a later quality-checked p close from the same series in the bounded post-entry, pre-kickoff window. Omitted when not measured.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_pct: Option<f64>,
/// Backend-formatted signed CLV percentage, present exactly when clv_pct is present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_display: Option<String>,
/// Backend-owned CLV formula text with the entry probability, close probability, and rounded result. Provider timestamps remain private.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_explanation: Option<String>,
/// Net return for the pick in stake units (return_usd / stake_usd - 1); one unit is one stake_usd stake, and the figure is the same under any stake size. Omitted when the outcome is not valued.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_score: Option<f64>,
/// Backend-formatted signed unit score, present exactly when unit_score is present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_score_display: Option<String>,
/// Recorded sharp-money share as a 0..1 fraction when available. This is not a winning probability.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sharp_pct: Option<f64>,
/// Recorded market-implied probability as a 0..1 fraction when available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub market_pct: Option<f64>,
/// Optional recorded specialist facts on a full response. These describe the trader, not the pick's win probability. Omitted when no recorded specialist is available.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub qualifying_expert: Option<PickOfTheDayQualifyingExpert>,
/// Public V1 S/A wallet count on the backed side, equal to sharp_wallet_count.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub traders: Option<i64>,
/// Recorded aggregate backed-side position value in USD when available, not a live balance or entry cost. The display_holders roster can include additional wallets; use the returned aggregate rather than summing that roster.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub backed_sharp_usd: Option<f64>,
/// Bounded S/A holder display projection from the recorded snapshot. Historical rows retain their recorded display shape.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub holders: Option<Vec<PickHolder>>,
/// Optional recorded holder roster in display order, with lead_backer first when present. Preserve the returned order. The roster can include wallets outside the recorded count and aggregate fields; do not derive holder_count, wallet counts or backed_sharp_usd from its length or a sum. Position values are gross holdings on the backed outcome and can coexist with holdings on the other outcome. Historical rows retain their recorded display shape.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_holders: Option<Vec<PickHolder>>,
/// S/A holder count for the public V1 compatibility projection. display_holders can include additional grades.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub holder_count: Option<i64>,
/// Display-ready explanation of the recorded pick. Render the returned text rather than reconstructing it from holder counts or grades.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub thesis: Option<String>,
/// Canonical web market URL.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub market_url: Option<String>,
/// Where this pick's outbound Polymarket link lands: Polymarket's own redirect answer for `/event/<event_slug>`, carrying the referral tag. Omitted until that redirect has been resolved; link to the event page instead when it is absent.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub polymarket_url: Option<String>,
/// The canonical /event game-page slug (one neutral page per game); omitted when the game has no neutral event page.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_slug: Option<String>,
/// Backend-resolved /event destination slug for the source market. Omitted outside full responses; null is an authoritative no-link decision.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_link_slug: Option<String>,
/// Provider-first sports context for the pick's market (team logos, league branding, live score). Full-state only; omitted when the pick is not a team-sports market.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sports_context: Option<PickSportsContext>,
/// Risk disclaimer shown with every pick.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub disclaimer: Option<String>,
/// Entitled published picks whose holder proof is unreadable, ordered by rank. Unauthorized ranks appear only in locked_picks and cannot trigger proof warming. Read retry_at for the next read.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proof_pending_picks: Option<Vec<ProofPendingPickSlot>>,
/// Optional full-response entry authorization. Missing or expired authorization cannot authorize an automated entry.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entry_authorization: Option<PotdEntryAuthorization>,
/// Unauthorized unresolved published or scheduled ranks. Contains no game, provider identity, price, or identifying clock. Pro may upgrade to Max to open these ranks.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub locked_picks: Option<Vec<PickOfTheDayLockedPicksItem>>,
/// Actionable status, including Upgrade to Max when only locked ranks are published.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
/// Stable pick row identity as decimal text. Never use a quality rank as identity.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_id: Option<String>,
/// Compatibility release slot. No quality claim; historic scheduling order is retained.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub publication_order: Option<i64>,
/// Viewer-independent free selection designation. New rows store it explicitly; historic null storage uses the original free slot.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_free_selection: Option<bool>,
/// Deprecated (#24157): always null. No replacement lineage was ever recorded in this field; the key stays so existing V1 clients keep parsing. Identify a pick by pick_id.
#[serde(default)]
pub supersedes_pick_id: Option<String>,
/// Optional full-only lead wallet publication facts. Omitted on legacy picks or when the recorded evidence is unavailable.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lead_backer: Option<PickLeadBacker>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayArchive {
/// Published Pick of the Day selections that have not been withdrawn, newest first by pick_date and then pick_rank within each product day. Withdrawn rows stay excluded after settlement.
pub picks: Vec<PickOfTheDayArchiveEntry>,
/// One entry per product day that has a published pick included in the archive, newest first, in the same order as picks. Each carries that day's net units, accumulated in the same backend pass and behind the same visibility gate as hit_rate.unit_score, so both cover the same population of picks. Re-adding the day totals reproduces hit_rate.unit_score to display precision rather than bit-for-bit, since that re-associates the floating-point sum.
pub days: Vec<PickOfTheDayArchiveDay>,
pub hit_rate: PickOfTheDayHitRate,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayArchiveDay {
/// The product day (YYYY-MM-DD).
pub date: String,
/// Published picks on the day: wins + losses + void + pending.
pub picks: i64,
/// Picks on the day that resolved as a win, counted by the same pass as hit_rate.wins, so the day entries sum to the headline record.
pub wins: i64,
/// Picks on the day that resolved as a loss, counted by the same pass as hit_rate.losses.
pub losses: i64,
/// Picks on the day that resolved void: the market settled with no winner, or the game was suspended or postponed and not completed within 72 hours of kickoff. A void is neither a win nor a loss and is not staked, so it enters no money figure.
pub void: i64,
/// Picks on the day not yet resolved. A day whose picks are all pending is a real 0-0 day with pending > 0, not a missing record.
pub pending: i64,
/// Net units over the day's visible, valued, staked (win or loss) picks: the same population staked_usd runs over. Omitted when the day has not scored -- every pick still pending or void, or a price-gated current row -- which is not the same as a real 0.0 day.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_score: Option<f64>,
/// unit_score pre-formatted as signed units to two decimals (for example +1.24u or -2.00u), by the same formatter the per-pick unit_score_display and hit_rate.unit_score_display use. Present exactly when unit_score is; unit_score is the source value.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_score_display: Option<String>,
/// win when every decided pick on the day won, loss when every one lost, over at least three decided (win or loss) picks with nothing pending. Omitted for every other day. A void is not a result: it neither lifts a day over the floor nor spoils a sweep.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sweep: Option<Sweep>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayArchiveEntry {
/// The pick's local publication date (YYYY-MM-DD).
pub pick_date: String,
/// Deprecated compatibility daily release slot; use pick_id for identity and publication_order for scheduling.
pub pick_rank: i64,
/// When this selection was published, as RFC3339 UTC. Use this instant for publication feeds and timelines; pick_date is the America/New_York product day, not a publication timestamp. Absent only for historical rows whose publication instant is unknown.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub published_at: Option<String>,
/// Human-readable matchup (e.g. "Portugal vs. Uzbekistan").
#[serde(default, skip_serializing_if = "Option::is_none")]
pub matchup: Option<String>,
/// Frozen canonical sport bucket (e.g. "Basketball", "MMA", or "Soccer"). Existing semantics are unchanged; presentation consumers should prefer display_category when present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
/// Frozen public presentation category. Tennis uses the provider-verified ATP or WTA tour, including Wimbledon and doubles; unknown tours retain Tennis. Every men's and women's cricket competition uses Cricket. Other sports use the curated league, esports title or soccer competition label, then the provider series name without its season year, then category when no competition is known. The canonical category and measured cohort are unchanged: an esports title still uses the pooled Esports category. Optional for mixed-version client compatibility.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_category: Option<String>,
/// Provider (Polymarket Gamma) market thumbnail URL (markets.image); omitted (not null) when the market has no image. Public regardless of the backed-side gate, so present for pending rows too.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image_url: Option<String>,
/// The backed side's outcome label. Omitted for a still-pending pick when the request is not from an authenticated Pro key.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pick_outcome_label: Option<String>,
/// Recorded top grade on the backed side in the public V1 compatibility projection. Omitted when unavailable. Historical values remain recorded snapshots.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_grade: Option<String>,
/// Settlement outcome of the backed side; 'pending' until the market resolves.
pub outcome: Outcome,
/// Pre-formatted settlement status for display: "Win" / "Loss" / "Void" / "Pending" -- the outcome enum above as a label, from the same formatter the pick payload's outcome_display uses. Convenience only; outcome is the source value.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_display: Option<String>,
/// When outcome was LAST written to a settled value (RFC3339 UTC), the same instant the commitment ledger publishes. It moves with a corrected market re-mapping an already-settled pick. Omitted (not null) for a pending pick and for a pick that settled before the instant was recorded, so absence means the instant is unknown, never that the pick is unsettled -- outcome answers that. Additive and optional for mixed-version client compatibility.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resolved_at: Option<String>,
/// The flat stake this row was valued at, in USD: 1000 since 2026-09-22 (100 before). Every row of the record is valued at the current stake, including picks published before the change. Present exactly when return_usd is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stake_usd: Option<f64>,
/// Gross return of stake_usd on this resolved pick: a win returns stake_usd / backed_price and a loss returns 0. A loss always returns 0 (the whole stake is lost regardless of price). The operand is published beside it as backed_price on every row that carries a return, so the entry never has to be recovered by inverting this number. Omitted (not null) for a still-pending pick, for a void (the market settled with no winner, or the game was suspended or postponed and not completed within 72 hours of kickoff, so the pick is not staked) and for a resolved WIN with no frozen price (a win's payout needs the price); mirrors the backend skip-when-absent behavior and the route-client optional (non-nullable) schema.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub return_usd: Option<f64>,
/// The same return on a literal $100 (a win returns 100 / backed_price, a loss 0), kept for compatibility: the field predates stake_usd and its name promises the $100 basis. Present exactly when return_usd is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub return_per_100: Option<f64>,
/// Pre-formatted return_usd as USD with cents: "$2,000.00". Present exactly when return_usd is -- it is formatted from that already-gated value -- so it is omitted for a still-pending pick, a void, an unpriced win, and any pick whose backed side is withheld. Convenience only; return_usd is the source value.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payout_display: Option<String>,
/// Frozen price of the backed side (0..1) that return_usd and return_per_100 were computed from: on a win, stake_usd / backed_price equals return_usd. It is the Polymarket CLOB order book midpoint at release, frozen write-once at publication, not an executed fill: a buyer lifts the ask, so a subscriber's own entry is usually a little worse than this price. Present exactly when return_usd is, and on a void, which carries no return: so it is omitted for a still-pending pick, an unpriced win, and any pick whose backed side is withheld.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub backed_price: Option<f64>,
/// Closing-line value toward the backed side, computed as (close / entry - 1) * 100. Omitted when the row is gated, ineligible, or not measured.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_pct: Option<f64>,
/// Backend-formatted signed CLV percentage, present exactly when clv_pct is present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_display: Option<String>,
/// Entry operand of clv_pct: the frozen pick probability the close is compared against. It equals backed_price, which CLV eligibility requires. Present exactly when clv_pct is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_entry_price: Option<f64>,
/// Close operand of clv_pct: the last valid Polymarket probability before the close bound (kickoff, or the first instant the live feed reported the game in progress when that came earlier). Present exactly when clv_pct is; the close timestamp stays private.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_close_price: Option<f64>,
/// Whether CLV applies. A visible resolved pick published after kickoff is not_applicable.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_applicability: Option<ClvApplicability>,
/// Backend-owned CLV text. A measured row names the entry, then the close, then the formula (close / entry - 1) x 100 and its rounded result; otherwise it gives the reason CLV does not apply or is unavailable.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_explanation: Option<String>,
/// Exact backend CLV capture disposition for this visible row. Pending and terminal provider or quality statuses are distinguishable; capture timestamps are never serialized, while a measured row's entry and close prices are published as clv_entry_price and clv_close_price.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_status: Option<String>,
/// Backend-owned CLV evidence basis. Source-null price-match bases preserve unknown original provenance. `historical_provider_nearby_price_match` requires a matching Polymarket point within five minutes before publication.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_basis: Option<String>,
/// Net return for this pick in stake units (return_usd / stake_usd - 1), where one unit is one stake_usd stake; the same figure under any stake size. Omitted when the backed side is withheld or the pick is not valued.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_score: Option<f64>,
/// Backend-formatted signed unit score, present exactly when unit_score is present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_score_display: Option<String>,
/// The account or paid tier needed to open this unresolved rank.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub required_tier: Option<RequiredTier>,
/// True for an unauthorized unresolved row. Game identity, category, image, publication clock and all backed facts are omitted.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub backed_side_locked: Option<bool>,
/// Stable pick row identity as decimal text. Never use a quality rank as identity.
pub pick_id: String,
/// Compatibility release slot. No quality claim; historic scheduling order is retained.
pub publication_order: i64,
/// Viewer-independent free selection designation. New rows store it explicitly; historic null storage uses the original free slot.
pub is_free_selection: bool,
/// Deprecated (#24157): always null. No replacement lineage was ever recorded in this field; the key stays so existing V1 clients keep parsing. Identify a pick by pick_id.
#[serde(default)]
pub supersedes_pick_id: Option<String>,
}
/// Select using the entry commitment_version, never implicit payload shape. All historic v1 hashes remain unchanged.
pub type PickOfTheDayCommitmentPayload = serde_json::Value;
/// The frozen identity of the pick, exactly as the hash was taken over it. Served byte for byte as it was hashed -- keys sorted by UTF-8 byte value, no insignificant whitespace -- so a verifier concatenates and hashes with nothing to reconstruct. Property order below is the wire order. The outcome is deliberately NOT part of it: surviving a corrected outcome unchanged is the case the commitment exists for. Worked example: {"backed_price":"0.545000","condition_id":"0xabc","kickoff":"2026-09-20T23:05:00Z","pick_date":"2026-09-20","pick_outcome_index":1,"pick_outcome_label":"Lakers","pick_rank":1,"platform":"polymarket"} with the nonce 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f hashes to 44d18fa5e2aa3a2bf3c971dcc9317c8ccbdfd5480a4773b6d8ffd5fbeeea84dc.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayCommitmentPayloadV1 {
/// Frozen pre-game price of the backed side, 0..1, as the plain decimal text of the stored NUMERIC at full stored precision, trailing zeros included. A string, never a number: a float round-trip would change the bytes and break the hash. Deliberately not normalized -- 0.545000 stays "0.545000".
pub backed_price: String,
/// Provider condition id of the backed market.
pub condition_id: String,
/// Frozen provider kickoff, whole seconds, UTC, literal Z. Fixed precision, never a shortest-lossless rendering.
pub kickoff: String,
/// ET product day (YYYY-MM-DD).
pub pick_date: String,
/// Index of the backed outcome within the market.
pub pick_outcome_index: i64,
/// Frozen display label of the backed outcome.
pub pick_outcome_label: String,
/// 1-based daily slot.
pub pick_rank: i64,
/// Provider platform. Always polymarket.
pub platform: String,
}
/// Version 2 rank-free canonical payload: sorted nine keys, unchanged string escaping, exact decimal text and whole-second UTC kickoff. pick_id is decimal text; version is JSON integer 2.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayCommitmentPayloadV2 {
/// Frozen pre-game price of the backed side, 0..1, as the plain decimal text of the stored NUMERIC at full stored precision, trailing zeros included. A string, never a number: a float round-trip would change the bytes and break the hash. Deliberately not normalized -- 0.545000 stays "0.545000".
pub backed_price: String,
/// Provider condition id of the backed market.
pub condition_id: String,
/// Frozen provider kickoff, whole seconds, UTC, literal Z. Fixed precision, never a shortest-lossless rendering.
pub kickoff: String,
/// ET product day (YYYY-MM-DD).
pub pick_date: String,
/// Index of the backed outcome within the market.
pub pick_outcome_index: i64,
/// Frozen display label of the backed outcome.
pub pick_outcome_label: String,
/// Provider platform. Always polymarket.
pub platform: String,
/// Stable pick row identity as decimal text. Never use a quality rank as identity.
pub pick_id: String,
pub version: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayHitRate {
/// Number of decided picks that won.
pub wins: i64,
/// Number of decided picks that lost.
pub losses: i64,
/// Number of decided picks (wins + losses); excludes void and pending.
pub decided: i64,
/// Rolling hit rate as a percentage (wins / decided * 100, to 1 decimal); 0 when none are decided.
pub pct: f64,
/// Number of picks that resolved void (excluded from the hit rate).
pub void: i64,
/// Number of picks still pending resolution (excluded from the hit rate).
pub pending: i64,
/// The flat stake every money figure here assumes, in USD: 1000 since 2026-09-22 (100 before). Always present.
pub stake_usd: f64,
/// Cumulative profit (USD) of a stake_usd-per-pick strategy over visible valued settled picks: a priced win pays stake_usd/backed_price - stake_usd, every visible loss pays -stake_usd independent of price. A void is not staked and adds nothing. An unpriced visible win and a current non-Insider row whose price is gated remain in wins/losses but are excluded from price-derived totals.
pub net_profit_usd: f64,
/// Total staked (USD) = stake_usd * count of visible valued settled picks: every visible loss and every priced win. Voids, unpriced wins and current non-Insider rows whose price is gated are excluded.
pub staked_usd: f64,
/// Return on the staked amount as a percentage (net_profit_usd / staked_usd * 100, to 1 decimal); 0 when nothing is staked.
pub roi_pct: f64,
/// Pre-formatted net profit for display, e.g. "+$100" / "-$40". Whole dollars, signed, round-then-signed so a rounds-to-zero record reads "+$0" (never "-$0"). Convenience only; net_profit_usd is the source value.
pub net_profit_display: String,
/// Pre-formatted ROI for display, e.g. "+8.3%" / "-20.0%". One decimal, signed, round-then-signed so a rounds-to-zero record reads "+0.0%" (never "-0.0%"). Convenience only; roi_pct is the source value.
pub roi_display: String,
/// Pre-formatted win rate for display, e.g. "92.3%". One decimal, unsigned. Convenience only; pct is the source value.
pub win_rate_display: String,
/// Cumulative net return in stake units over the same valued win/loss population as net_profit_usd.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_score: Option<f64>,
/// Backend-formatted signed cumulative unit score, for example "+1.25u". Omitted when no valued settled pick contributes to the aggregate.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unit_score_display: Option<String>,
/// Visible resolved archive rows with no CLV capture disposition yet.
pub clv_pending: i64,
/// Visible resolved archive rows with a terminal non-measured CLV disposition.
pub clv_unavailable: i64,
/// Aggregate CLV state. Counts remain available for mixed measured/pending/unavailable populations.
pub clv_state: ClvState,
/// Visible resolved archive rows with valid basis-specific CLV entry evidence. Known-source bases require CLOB provenance; historical_provider_price_match instead requires source provenance to remain null and the latest provider p entry in the one-hour window at or before publication to match the stored display.
pub clv_eligible: i64,
/// Every visible resolved archive row, including post-kickoff picks. A price-gated current row is excluded until its backed side becomes visible.
pub clv_total: i64,
/// Eligible resolved public rows with a measured Polymarket CLOB close.
pub clv_measured: i64,
/// Visible resolved rows published before kickoff. This is the CLV coverage denominator.
pub clv_applicable: i64,
/// Visible resolved rows published after kickoff. These rows remain public but do not enter coverage.
pub clv_not_applicable: i64,
/// Measured divided by clv_applicable as a percentage. Omitted when clv_applicable is zero.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_coverage_pct: Option<f64>,
/// Backend-formatted measured/clv_applicable coverage percentage.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_coverage_display: Option<String>,
/// Backend-owned coverage count text, including the excluded post-kickoff count.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_coverage_explanation: Option<String>,
/// Arithmetic mean of per-pick ratio CLV, (close / entry - 1) * 100. A cheap entry weighs more here than a favorite, so the headline average is clv_avg_pp; this field is kept for continuity. Suppressed until at least five measured rows exist.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_avg_pct: Option<f64>,
/// Backend-formatted signed average CLV percentage, present when clv_avg_pct is present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_avg_display: Option<String>,
/// Arithmetic mean of the measured closing-line move in percentage points, (close - entry) * 100, so every pick counts on the same scale. The headline CLV average. Covers the same measured rows as clv_avg_pct and is suppressed under the same five-row floor.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_avg_pp: Option<f64>,
/// Backend-formatted signed average in percentage points, for example "+0.4 pp"; a value that rounds to zero reads "0.0 pp". Present when clv_avg_pp is present.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub clv_avg_pp_display: Option<String>,
/// Measured public rows where close is strictly greater than entry.
pub clv_beats_close: i64,
/// Measured public rows where close exactly equals entry.
pub clv_ties: i64,
/// Cumulative track-record series, one point per decided (win/loss) pick in ascending pick_date order (pending and void picks add no point). A non-valued decided row carries cumulative profit forward while advancing hit rate. The last point's net_profit_usd and hit_rate_pct equal the headline net_profit_usd and pct by construction. Empty when nothing is decided.
pub series: Vec<PickOfTheDayHitRateSeriesItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayHitRateSeriesItem {
/// The decided pick's publish date (YYYY-MM-DD).
pub date: String,
/// Running cumulative stake_usd-per-pick profit (USD) through this pick; valued losses and priced wins are booked, while a non-valued decided row carries profit forward unchanged.
pub net_profit_usd: f64,
/// Running rolling hit rate (wins / decided * 100, to 1 decimal) through this pick.
pub hit_rate_pct: f64,
}
/// The commitment ledger: every published pick, ascending, in the state its commitment is actually in. The counts are derived from entries in the same pass that builds it.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayLedger {
/// One entry per published (pick_date, pick_rank), ascending by pick_date then pick_rank.
pub entries: Vec<PickOfTheDayLedgerEntry>,
/// Number of entries, all states included.
pub entry_count: i64,
/// Entries committed to and not yet settled.
pub sealed_count: i64,
/// Entries committed to and verifiable now.
pub opened_count: i64,
/// Entries carrying no commitment, so provable by nothing: the honest size of the unprovable part of the record. It only stops growing; no pick is ever retro-sealed.
pub uncommitted_count: i64,
}
/// One ledger entry. Read `state` to know which shape you have; the three are disjoint.
///
/// Discriminated by `state`. A `state` this release does not know is kept whole in
/// `Unknown`, so a new kind never fails a response.
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum PickOfTheDayLedgerEntry {
/// `state` is `sealed`.
Sealed(PickOfTheDayLedgerSealedEntry),
/// `state` is `opened`.
Opened(PickOfTheDayLedgerOpenedEntry),
/// `state` is `uncommitted`.
Uncommitted(PickOfTheDayLedgerUncommittedEntry),
/// A `state` this release does not know, kept as sent.
Unknown(serde_json::Value),
}
impl<'de> Deserialize<'de> for PickOfTheDayLedgerEntry {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = serde_json::Value::deserialize(deserializer)?;
match value.get("state").and_then(serde_json::Value::as_str) {
Some("sealed") => serde_json::from_value(value)
.map(Self::Sealed)
.map_err(de::Error::custom),
Some("opened") => serde_json::from_value(value)
.map(Self::Opened)
.map_err(de::Error::custom),
Some("uncommitted") => serde_json::from_value(value)
.map(Self::Uncommitted)
.map_err(de::Error::custom),
_ => Ok(Self::Unknown(value)),
}
}
}
impl Serialize for PickOfTheDayLedgerEntry {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Sealed(inner) => inner.serialize(serializer),
Self::Opened(inner) => inner.serialize(serializer),
Self::Uncommitted(inner) => inner.serialize(serializer),
Self::Unknown(inner) => inner.serialize(serializer),
}
}
}
/// A settled pick whose commitment is open: the nonce plus the exact payload the hash was taken over. Concatenate the payload bytes as received with the decoded nonce and sha256 them to reproduce commitment_hash.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayLedgerOpenedEntry {
pub state: String,
/// ET product day the pick belongs to (YYYY-MM-DD).
pub pick_date: String,
/// Deprecated compatibility daily release slot; use pick_id for identity and publication_order for scheduling.
pub pick_rank: i64,
/// sha256(canonical_json(payload) || nonce), lowercase hex, no 0x prefix. Publishable the moment the pick releases: without the nonce it is not invertible.
pub commitment_hash: String,
/// The 32-byte nonce the hash was taken over, lowercase hex, no 0x prefix. Secret while the pick is live: a pick payload is low entropy, so a published nonce on a live pick would hand out the backed side.
pub commitment_nonce: String,
/// The construction the hash was taken with, stated in the response so a verifier never has to guess the serialization.
pub commitment_algo: String,
/// When the hash was frozen. Always strictly before kickoff: a pick that reaches kickoff unsealed stays unsealed forever, because a seal written after the game started would be a backdated proof.
pub sealed_at: String,
/// When outcome was LAST written to a settled value, or null when that instant is unknown. It moves with a corrected market re-mapping an already-settled pick, while commitment_hash stays untouched -- which is how a mirror that keeps history sees a correction.
#[serde(default)]
pub resolved_at: Option<String>,
/// The frozen provider kickoff in the canonical payload form: whole seconds, UTC, literal Z. This exact string reappears inside payload.kickoff when the pick opens.
pub kickoff: String,
pub payload: PickOfTheDayCommitmentPayload,
/// How the pick settled. Never pending: a pending pick is a sealed entry.
pub outcome: PickOfTheDayLedgerOpenedEntryOutcome,
/// Why the site withdrew this pick from its track record, or null when the pick counts. provider_game_cancelled: Polymarket marked the game Cancelled while the pick was pending. published_after_kickoff: the pick was published at or after its game started, including a suspended match that resumed later, so it was never a pre-game pick; this can be set after the pick settled. A withdrawn pick keeps its commitment and its real outcome here, but the site's record leaves it out of every count and money figure. Not part of the hashed payload, so it never moves commitment_hash.
#[serde(default)]
pub withdrawal_reason: Option<WithdrawalReason>,
/// Frozen matchup, for a reader.
pub matchup: String,
/// Frozen canonical sport bucket (for example Basketball or MMA), not the exact public league identity; the archive owns that.
pub category: String,
/// The pick's public page.
pub permalink: String,
/// Stable pick row identity as decimal text. Never use a quality rank as identity.
pub pick_id: String,
/// Compatibility release slot. No quality claim; historic scheduling order is retained.
pub publication_order: i64,
/// Viewer-independent free selection designation. New rows store it explicitly; historic null storage uses the original free slot.
pub is_free_selection: bool,
/// Deprecated (#24157): always null. No replacement lineage was ever recorded in this field; the key stays so existing V1 clients keep parsing. Identify a pick by pick_id.
#[serde(default)]
pub supersedes_pick_id: Option<String>,
/// Explicit proof provenance: 1 retains historic eight-field canonical JSON; 2 binds stable pick_id and version without pick_rank.
pub commitment_version: i64,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum PickOfTheDayLedgerOpenedEntryOutcome {
/// `win`
#[serde(rename = "win")]
Win,
/// `loss`
#[serde(rename = "loss")]
Loss,
/// `void`
#[serde(rename = "void")]
Void,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl PickOfTheDayLedgerOpenedEntryOutcome {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Win => "win",
Self::Loss => "loss",
Self::Void => "void",
Self::Other(value) => value,
}
}
}
impl From<&str> for PickOfTheDayLedgerOpenedEntryOutcome {
fn from(value: &str) -> Self {
match value {
"win" => Self::Win,
"loss" => Self::Loss,
"void" => Self::Void,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for PickOfTheDayLedgerOpenedEntryOutcome {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for PickOfTheDayLedgerOpenedEntryOutcome {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// An unresolved published pick. Carries only date, rank, hash, algorithm, seal instant and permalink. Kickoff and game identity are withheld; seal time remains public commitment provenance.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayLedgerSealedEntry {
pub state: String,
/// ET product day the pick belongs to (YYYY-MM-DD).
pub pick_date: String,
/// Deprecated compatibility daily release slot; use pick_id for identity and publication_order for scheduling.
pub pick_rank: i64,
/// sha256(canonical_json(payload) || nonce), lowercase hex, no 0x prefix. Publishable the moment the pick releases: without the nonce it is not invertible.
pub commitment_hash: String,
/// The construction the hash was taken with, stated in the response so a verifier never has to guess the serialization.
pub commitment_algo: String,
/// When the hash was frozen. Always strictly before kickoff: a pick that reaches kickoff unsealed stays unsealed forever, because a seal written after the game started would be a backdated proof.
pub sealed_at: String,
/// The pick's public page.
pub permalink: String,
/// Stable pick row identity as decimal text. Never use a quality rank as identity.
pub pick_id: String,
/// Compatibility release slot. No quality claim; historic scheduling order is retained.
pub publication_order: i64,
/// Viewer-independent free selection designation. New rows store it explicitly; historic null storage uses the original free slot.
pub is_free_selection: bool,
/// Deprecated (#24157): always null. No replacement lineage was ever recorded in this field; the key stays so existing V1 clients keep parsing. Identify a pick by pick_id.
#[serde(default)]
pub supersedes_pick_id: Option<String>,
/// Explicit proof provenance: 1 retains historic eight-field canonical JSON; 2 binds stable pick_id and version without pick_rank.
pub commitment_version: i64,
}
/// A pick without a commitment, retained so the record cannot omit unprovable entries. Unresolved entries omit game identity; once resolved, matchup, category and payload become public. Existing opened canonical payload bytes and hashes are unchanged.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayLedgerUncommittedEntry {
pub state: String,
/// ET product day the pick belongs to (YYYY-MM-DD).
pub pick_date: String,
/// Deprecated compatibility daily release slot; use pick_id for identity and publication_order for scheduling.
pub pick_rank: i64,
/// Always true: this pick has no commitment and never will.
pub pre_commitment: bool,
/// How the pick settled, or pending.
pub outcome: Outcome,
/// Omitted while the pick is pending; once it settles, why the site withdrew it from its track record, or null when the pick counts. provider_game_cancelled: Polymarket marked the game Cancelled while the pick was pending. published_after_kickoff: the pick was published at or after its game started, including a suspended match that resumed later, so it was never a pre-game pick; this can be set after the pick settled. A withdrawn pick stays here with its real outcome, but the site's record leaves it out of every count and money figure.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub withdrawal_reason: Option<WithdrawalReason>,
/// Frozen matchup, for a reader.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub matchup: Option<String>,
/// Frozen canonical sport bucket (for example Basketball or MMA), not the exact public league identity; the archive owns that.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
/// When outcome was LAST written to a settled value, or null when that instant is unknown. It moves with a corrected market re-mapping an already-settled pick, while commitment_hash stays untouched -- which is how a mirror that keeps history sees a correction.
#[serde(default)]
pub resolved_at: Option<String>,
/// The pick's market, side and price once it has settled; null while it is pending, and null for a settled pick whose stored row lacks one of these columns. Not hashed: nothing was committed over these values, which is what pre_commitment: true says.
#[serde(default)]
pub payload: Option<PickOfTheDayUncommittedPayload>,
/// The pick's public page.
pub permalink: String,
/// Stable pick row identity as decimal text. Never use a quality rank as identity.
pub pick_id: String,
/// Compatibility release slot. No quality claim; historic scheduling order is retained.
pub publication_order: i64,
/// Viewer-independent free selection designation. New rows store it explicitly; historic null storage uses the original free slot.
pub is_free_selection: bool,
/// Deprecated (#24157): always null. No replacement lineage was ever recorded in this field; the key stays so existing V1 clients keep parsing. Identify a pick by pick_id.
#[serde(default)]
pub supersedes_pick_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayLockedPicksItem {
pub pick_rank: i64,
pub required_tier: String,
}
/// A successful current-day entitlement response when only unauthorized ranks have published. It carries an empty pick set, identity-free locked ranks and an upgrade message. Selection IDs, game identity, prices and unauthorized clocks are absent. Any scheduled or proof-pending rows are entitled rows.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayNoEntitledPicks {
/// Only locked ranks are published for this account.
pub state: String,
/// Current product date in America/New_York (YYYY-MM-DD).
pub pick_date: String,
/// Empty: no entitled proof-readable picks are returned.
pub picks: Vec<PickOfTheDay>,
/// Zero entitled proof-readable picks.
pub pick_count: i64,
/// Unauthorized unresolved published or scheduled ranks. Contains no game, provider identity, price, or identifying clock. Pro may upgrade to Max to open these ranks.
pub locked_picks: Vec<PickOfTheDayNoEntitledPicksLockedPicksItem>,
/// Actionable status, including Upgrade to Max when only locked ranks are published.
pub message: String,
/// Rank-ordered entitled selections that have not released. Every row retains release_at and kickoff; unauthorized scheduled ranks appear only in identity-free locked_picks.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scheduled_picks: Option<Vec<ScheduledPickSlot>>,
/// Entitled published picks whose holder proof is unreadable, ordered by rank. Unauthorized ranks appear only in locked_picks and cannot trigger proof warming. Read retry_at for the next read.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proof_pending_picks: Option<Vec<ProofPendingPickSlot>>,
/// Deprecated (#24157): always null. No replacement lineage was ever recorded in this field; the key stays so existing V1 clients keep parsing. Identify a pick by pick_id.
#[serde(default)]
pub supersedes_pick_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayNoEntitledPicksLockedPicksItem {
pub pick_rank: i64,
pub required_tier: String,
}
/// Optional recorded specialist facts on a full response. These describe the trader, not the pick's win probability. Omitted when no recorded specialist is available.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayQualifyingExpert {
/// Trader wallet address.
pub address: String,
/// Provider display name, or null for an unnamed wallet.
#[serde(default)]
pub name: Option<String>,
/// Recorded trader grade. Public V1 preserves its S/A compatibility projection.
#[serde(default)]
pub grade: Option<String>,
/// The canonical sport bucket the win rate was measured over (for example Basketball). Can be BROADER than the pick's display_category, which names an exact league such as NBA — label the rate with this field, never with display_category.
pub canonical_category: String,
/// Share of the trader’s resolved markets in canonical_category with positive realized P&L, as a 0..1 fraction. Null when not measured. This is a trader statistic, not the pick’s probability of winning.
#[serde(default)]
pub win_rate: Option<f64>,
/// Number of resolved markets behind win_rate; null when not measured.
#[serde(default)]
pub n_resolved: Option<i64>,
/// Recorded Polymarket position value on the backed outcome, in USD. It can change after this snapshot.
pub position_usd: f64,
/// Recorded position value on the other outcome of this market, in USD. Omitted when not recorded; zero is a measured value.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub opposite_position_usd: Option<f64>,
/// Timestamp of the recorded trader statistics.
pub stats_computed_at: String,
}
/// A settled uncommitted pick's market, side and price. The same eight fields as PickOfTheDayCommitmentPayload, in the same key order, so a settled pick's side and price sit under payload whatever the entry's state. It is NOT a commitment: no hash was taken over it before the game, and it proves nothing about when the pick was made.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickOfTheDayUncommittedPayload {
/// Frozen pre-game price of the backed side, 0..1, as the plain decimal text of the stored NUMERIC at full stored precision, trailing zeros included -- rendered exactly as the commitment payload renders it.
pub backed_price: String,
/// Provider condition id of the backed market.
pub condition_id: String,
/// Frozen provider kickoff, UTC, literal Z; null when no kickoff was frozen. Whole seconds render exactly as the commitment payload does; a sub-second instant keeps its fraction rather than being truncated, since nothing here is hashed.
#[serde(default)]
pub kickoff: Option<String>,
/// ET product day (YYYY-MM-DD).
pub pick_date: String,
/// Index of the backed outcome within the market.
pub pick_outcome_index: i64,
/// Frozen display label of the backed outcome.
pub pick_outcome_label: String,
/// 1-based daily slot.
pub pick_rank: i64,
/// Provider platform. Always polymarket.
pub platform: String,
}
/// Provider-first sports context for a Pick of the Day market: team crests, league branding, and live score. Team logos and league logo are provider-owned (Polymarket /teams crests for clubs, country flags for national teams and tennis players); no local derivation.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickSportsContext {
/// League or competition display name (e.g. "Premier League").
#[serde(default)]
pub league_name: Option<String>,
/// Provider-owned event taxonomy from Gamma eventMetadata, joined in league · serie · tournament order with blanks and case-insensitive duplicates removed. Separate from league_name; omitted when the provider does not supply the metadata.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub competition_label: Option<String>,
/// League logo URL (provider-owned).
#[serde(default)]
pub league_logo: Option<String>,
/// The team mapped to the market's YES outcome, or the parent-event home/first team when event_matchup is true.
#[serde(default)]
pub yes_team: Option<PickSportsTeam>,
/// The team mapped to the market's NO outcome, or the parent-event away/second team when event_matchup is true.
#[serde(default)]
pub no_team: Option<PickSportsTeam>,
/// Provider game identifier (Polymarket Gamma gameId); omitted when the provider supplies none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub game_id: Option<i64>,
/// Always present. True when the two teams are the parent-event match identity for a teamless binary leg (e.g. a draw, totals, or prop market), not the market's own outcomes.
pub event_matchup: bool,
/// Present only alongside event_matchup: the event team the binary leg is about (provider group_item_title matched to a matchup team, e.g. Belgium for "Will Belgium win?"), i.e. the winner on a Yes resolution. Omitted for teamless legs (draw, totals, prop).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_subject_team: Option<PickSportsTeam>,
/// The two teams as a single whole-game label, joined "<home> – <away>" (en-dash) in provider display order (e.g. "Portugal – Uzbekistan"). Composed server-side from the provider team names (no title/slug parsing). Present when both teams resolve a name; omitted for single-subject, teamless, or non-two-team contexts.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub matchup_title: Option<String>,
}
/// A single sports team or competitor in a Pick of the Day market's sports context. Identity and score fields are provider-owned and nullable. The structured score fields (`sets`, `format`, `sets_won`) and the tennis fields (`headshot`, `headshot_revision`, `tour`) are backend-owned and are OMITTED rather than null when they do not apply, so a consumer must treat an absent key and a null the same way.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PickSportsTeam {
/// Team display label as it appears on the market outcome (e.g. "Portugal").
#[serde(default)]
pub label: Option<String>,
/// Abbreviated team label (e.g. "POR").
#[serde(default)]
pub short_label: Option<String>,
/// Full team or competitor name (e.g. "Portugal national football team").
#[serde(default)]
pub full_name: Option<String>,
/// Provider team identifier (Polymarket /teams id).
#[serde(default)]
pub provider_id: Option<i64>,
/// Team crest or flag URL. Official NFL (32), WNBA (15), NBA (30), NHL (32), and MLB (30) club crests use same-origin relative paths (`/api/sports/team-logos/{league}/{abbr}.svg?v=<content hash>`); resolve relative URLs against the API origin. Vendored soccer club crests use the same path with `.png`. Other teams use provider artwork, including country flags for national teams and tennis players. Novelty and national-team rows outside the vendored club sets retain provider artwork.
#[serde(default)]
pub logo: Option<String>,
/// True when artwork measurements show that the team mark needs a light plate on a dark background. Vendored crest verdicts are tied to the exact asset bytes; provider artwork is measured by the teams sync. Always sent; false when no current measurement marks the logo dark.
pub logo_mark_dark: bool,
/// Team brand color as a hex string (provider-owned).
#[serde(default)]
pub color: Option<String>,
/// Win-loss record as a display string (e.g. "12-4").
#[serde(default)]
pub record: Option<String>,
/// Live or final score as a display string when the game is in play or settled.
#[serde(default)]
pub score: Option<String>,
/// Tennis player headshot URL, served same-origin. Present only for a tennis competitor the headshot resolver matched; absent for team sports and for unmatched players, where `logo` stays the fallback.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub headshot: Option<String>,
/// Tennis tour this competitor belongs to. Present for every tennis entry whether or not `headshot` resolved, so a consumer can tell a tennis player with no photo from a non-tennis team. Absent for every other sport. Only `atp` and `wta` name a gender; the ITF World Tennis Tour runs men's and women's events and the provider does not say which, so `itf` means tennis with gender unknown.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tour: Option<Tour>,
/// Per-set score cells for this side, in set order. Backend-owned: render these rather than parsing `score`. Omitted entirely when the provider score is not a structured multi-set match or could not be parsed, so an absent array and an empty one carry the same meaning.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sets: Option<Vec<ScoreCell>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub format: Option<ScoreFormat>,
/// Completed sets won by this side. Present only when both sides expose the same set columns, so a partially parsed scoreline reports no tally rather than a misleading one.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sets_won: Option<i64>,
/// Optional monotonic photo revision for this tennis player, independent of the sports score revision. Legacy stored photos start at zero. Successful new or changed image bytes advance it; source checks and attribution changes do not. Compare only for the same tour and provider_id: a higher revision replaces the portrait, an equal revision may fill a missing portrait, and a lower revision must not replace a newer one. Absent when no photo resolved.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub headshot_revision: Option<i64>,
/// Optional latest retrieved ATP/WTA singles rank for this player. Absent for unranked, ambiguous, doubles, expired, or unavailable identities. This is not rank at match time.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ranking: Option<TennisRanking>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Platform {
/// `polymarket`
#[serde(rename = "polymarket")]
Polymarket,
/// `all`
#[serde(rename = "all")]
All,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Platform {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Polymarket => "polymarket",
Self::All => "all",
Self::Other(value) => value,
}
}
}
impl From<&str> for Platform {
fn from(value: &str) -> Self {
match value {
"polymarket" => Self::Polymarket,
"all" => Self::All,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Platform {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Platform {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PlatformCapabilities {
pub grade: PlatformCapabilityStatus,
pub pnl: PlatformCapabilityStatus,
pub strategy: PlatformCapabilityStatus,
pub timeline: PlatformCapabilityStatus,
/// The large-trade feed. Canonical key since #16304; whale_signal is its deprecated spelling, emitted beside it with the same value.
pub large_trades: PlatformCapabilityStatus,
pub whale_signal: PlatformCapabilityStatus,
pub suspicious_trades: PlatformCapabilityStatus,
pub insider_radar: PlatformCapabilityStatus,
pub market_snapshot: PlatformCapabilityStatus,
}
/// Every capability the API serves reports supported. The field names a per-capability status so a client can branch on coverage; no other value is emitted.
pub type PlatformCapabilityStatus = String;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Platforms {
pub platforms: PlatformsPlatforms,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PlatformsPlatforms {
pub polymarket: PlatformCapabilities,
}
/// A derived 0xinsider correction to Polymarket's P&L, never a Polymarket value. Polymarket misbooks neg-risk conversions, in both directions: it puts cost on placeholder YES outcomes minted by a conversion and never books it as a loss (negative amount), and its position records leave out shares the wallet really got, such as YES minted by a conversion, so it books no P&L on them (positive amount). Per neg-risk market the wallet converted in, 0xinsider measures Polymarket's own activity cash against the sum of its position P&L, and sums the difference over every measured market whose shares did not move by direct transfer: transferred shares have no activity row, so such a market keeps Polymarket's figure and is counted in markets_excluded. It is published once every neg-risk market the wallet converted in is measured and checked for transfers, and stays as last published until then (on the trader body, pnl.correction_pending says so); a sum under $1 either way publishes nothing. The body's own P&L fields keep Polymarket's figures; this object adds the amount and the corrected figures. Money is truncated toward zero to cents.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PnlCorrection {
/// The correction method.
pub basis: String,
/// The correction in USD. Negative when Polymarket overstates the wallet.
pub amount: f64,
/// Polymarket's net realized P&L plus amount: the figure 0xinsider grades on. Omitted when Polymarket's figure is.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub realized: Option<f64>,
/// Polymarket's net total P&L plus amount: the 0xinsider profile's Total P&L. Omitted when Polymarket's figure is, and on leaderboard entries.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub net_total: Option<f64>,
/// The newest measurement among the measured markets.
pub as_of: String,
/// Neg-risk markets whose measurement amount sums.
pub markets_measured: i64,
/// Measured neg-risk markets left out because shares moved in or out of the wallet by direct transfer; they keep Polymarket's figure. Omitted when none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub markets_excluded: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Position {
/// Composite prefixed ID `pos_<wallet>:<condition_id>:<outcome_index>`.
pub id: String,
/// Provider discriminator. Polymarket only.
pub platform: String,
/// Lowercased proxy wallet address.
pub wallet: String,
/// Binary outcome side. Non-binary positions are not surfaced on V1.
pub side: Direction,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for this outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Live share count from the wallet_positions mirror.
pub shares: f64,
/// Volume-weighted entry price for this leg.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub avg_price: Option<f64>,
/// Current mark-to-market value in USD (always non-null on V1 — pre-reconcile rows are excluded).
pub current_value_usd: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initial_value_usd: Option<f64>,
/// Unrealized P&L for the open position (Polymarket `cashPnl`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cash_pnl: Option<f64>,
/// Closed-leg P&L rolled up (Polymarket `realizedPnl`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub realized_pnl: Option<f64>,
/// Additive lossless source atoms for the display-safe position fields. Parse every `value` with decimal-safe arithmetic; never recover exact values from the numeric twins.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exact: Option<PositionExact>,
/// Max updated_at across mirror legs for this pair.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_reconciled_at: Option<String>,
/// Backend-computed staleness bucket derived from last_reconciled_at.
pub freshness: Freshness,
pub trader: PositionTrader,
pub market: PositionMarket,
}
/// Lossless position atoms from `wallet_positions`. `shares` and `current_value_usd` are required when this object is present; other source values are omitted when the mirror has no verified value.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PositionExact {
pub shares: ExactDecimal,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cost_basis_usd: Option<ExactDecimal>,
/// Exact derived price: `wallet_positions.cost_basis_usd / wallet_positions.shares`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub avg_price: Option<ExactDecimal>,
pub current_value_usd: ExactDecimal,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initial_value_usd: Option<ExactDecimal>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cash_pnl: Option<ExactDecimal>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub realized_pnl: Option<ExactDecimal>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PositionMarket {
/// Prefixed ID (`mkt_...`).
pub id: String,
pub condition_id: String,
pub title: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_slug: Option<String>,
/// Provider-backed market_canonical category.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
/// Provider-reported outcome label (e.g. team name for sports). Separate from `side` because provider labels can diverge from the binary Yes/No axis.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome_label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PositionTimelineEvent {
/// Prefixed ID (pe_...).
pub id: String,
/// ISO 8601 timestamp of the on-chain fill.
pub event_timestamp: String,
/// The wallet's own side of this fill: buy or sell.
pub action: Action,
pub outcome_side: Side,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for this outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Signed share delta (+ on buy, − on sell).
pub amount_delta: f64,
/// Fill price in USDC per share, in [0,1].
pub price: f64,
/// Positive USDC notional of the fill.
pub usdc_notional: f64,
/// Polygon transaction hash of the fill.
pub tx_hash: String,
/// Cumulative signed stored-fill amount for this outcome after this fill, including older pages. Stable across limits/cursors for unchanged source history. Excludes split, merge, redemption and neg-risk activity; not complete holdings. Incomplete fill history can also leave a negative amount.
pub running_amount: f64,
/// Cumulative buy-weighted average of stored fills for this outcome, including older pages; not provider current-position avgPrice. Sells do not change this value. 0 when no stored buys have occurred yet.
pub running_avg_price: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PositionTrader {
/// Prefixed ID (`trd_...`).
pub id: String,
pub address: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grade: Option<Grade>,
/// Win rate as a ratio from 0 to 1 (0.62 means 62%), the same scale as stats.win_rate on GET /api/v1/trader/{address}. Absent when unknown.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub win_rate: Option<f64>,
/// Polymarket's net total P&L for the wallet in USD: native Polymarket position P&L across all markets, fees included, plus credited maker and taker rebates (trader_trading_pnl.net_total_pnl). The 0xinsider profile's Total P&L is this figure unless the wallet has a published correction for neg-risk conversions, which GET /api/v1/trader/{address} serves as pnl.correction; the profile then shows pnl.correction.net_total. Not the Polymarket leaderboard figure, which GET /api/v1/trader/{address} still serves as pnl.total. Omitted when the wallet has no native accounting snapshot; the leaderboard figure is never substituted.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pnl: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub markets: Option<i64>,
/// Days since the wallet's first-seen time. For a wallet 0xinsider first recorded on or after March 21, 2026, that is when 0xinsider recorded it. For a wallet recorded earlier, it is usually the wallet's first Polymarket activity, so the age can run to years. It is not the wallet's on-chain age or the time of its first trade. Absent when the first-seen time is unknown.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub wallet_age_days: Option<f64>,
/// True when wallet_age_days is under the new-wallet threshold (30 days), which means 0xinsider first recorded the wallet in the last 30 days. False when wallet_age_days is absent. This public V1 field is first-seen recency, not the wallet's on-chain age.
pub is_new_wallet: bool,
}
/// Returned entry permission bound to the named market, token and outcome. Honor the immutable max_entry_price and expires_at, and quote a current executable book for the actual stake. The submitted order price must remain within the returned limit; fills may be lower. Fees are separate, and this grant does not guarantee liquidity, execution or positive expected value.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PotdEntryAuthorization {
pub version: i64,
pub authorization_id: String,
/// Identifier for the policy recorded with this authorization. It does not replace the returned max_entry_price or expires_at. Existing grants retain their recorded values.
pub policy_version: i64,
pub condition_id: String,
pub token_id: String,
pub outcome_index: i64,
/// Canonical sport bucket, not display_category.
pub category: String,
/// Exact provider parent event ID, or provider event ID when no parent exists.
pub canonical_event_id: String,
/// Maximum authorized order price as an exact decimal string, excluding fees. Quote a current executable book for the actual stake and keep the submitted order price at or below this bound. Actual fills may be lower; this limit does not guarantee a fill or define fair probability.
pub max_entry_price: String,
/// Selected-token best ask from the recorded reference book at issuance. This is a reference quote, not the authorized order-price limit; use max_entry_price for that limit.
pub reference_best_ask: String,
pub reference_book_hash: String,
pub reference_book_at: String,
pub issued_at: String,
/// Authorization expiry timestamp. Existing grants do not extend; an expired authorization cannot authorize a new automated entry.
pub expires_at: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PrepareWebhookSecretResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum PriceProvenance {
/// `gamma_outcome_prices`
#[serde(rename = "gamma_outcome_prices")]
GammaOutcomePrices,
/// `clob_display`
#[serde(rename = "clob_display")]
ClobDisplay,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl PriceProvenance {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::GammaOutcomePrices => "gamma_outcome_prices",
Self::ClobDisplay => "clob_display",
Self::Other(value) => value,
}
}
}
impl From<&str> for PriceProvenance {
fn from(value: &str) -> Self {
match value {
"gamma_outcome_prices" => Self::GammaOutcomePrices,
"clob_display" => Self::ClobDisplay,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for PriceProvenance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for PriceProvenance {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// One PUBLISHED same-day pick whose holder proof is not readable yet: its stable slot rank, the release and kickoff instants, and the instant before which a retry cannot succeed. Every item in `picks` carries its full required shape, so a pick that cannot meet it is listed here instead of being served with missing fields or a synthetic zero.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ProofPendingPickSlot {
/// Deprecated compatibility daily release slot; use pick_id for identity and publication_order for scheduling.
pub pick_rank: i64,
/// The pick's stored release instant.
pub release_at: String,
/// The backed game's frozen kickoff instant; absent for a legacy row without one.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub kickoff: Option<String>,
/// Recommended next read: 30 seconds ahead while pre-game proof is warming, one hour ahead for a post-kickoff pending legacy row that only settlement can make readable. Schedule against it instead of polling.
pub retry_at: String,
/// Stable pick row identity as decimal text. Never use a quality rank as identity.
pub pick_id: String,
/// Compatibility release slot. No quality claim; historic scheduling order is retained.
pub publication_order: i64,
/// Viewer-independent free selection designation. New rows store it explicitly; historic null storage uses the original free slot.
pub is_free_selection: bool,
/// Deprecated (#24157): always null. No replacement lineage was ever recorded in this field; the key stays so existing V1 clients keep parsing. Identify a pick by pick_id.
#[serde(default)]
pub supersedes_pick_id: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ProviderReadSource {
/// `cached`
#[serde(rename = "cached")]
Cached,
/// `live`
#[serde(rename = "live")]
Live,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ProviderReadSource {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Cached => "cached",
Self::Live => "live",
Self::Other(value) => value,
}
}
}
impl From<&str> for ProviderReadSource {
fn from(value: &str) -> Self {
match value {
"cached" => Self::Cached,
"live" => Self::Live,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ProviderReadSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ProviderReadSource {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum RankingSource {
/// `live`
#[serde(rename = "live")]
Live,
/// `db_only`
#[serde(rename = "db_only")]
DbOnly,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl RankingSource {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Live => "live",
Self::DbOnly => "db_only",
Self::Other(value) => value,
}
}
}
impl From<&str> for RankingSource {
fn from(value: &str) -> Self {
match value {
"live" => Self::Live,
"db_only" => Self::DbOnly,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for RankingSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for RankingSource {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Reason {
/// `outcome_prices_missing`
#[serde(rename = "outcome_prices_missing")]
OutcomePricesMissing,
/// `outcome_price_leg_missing`
#[serde(rename = "outcome_price_leg_missing")]
OutcomePriceLegMissing,
/// `zero_price_sentinel`
#[serde(rename = "zero_price_sentinel")]
ZeroPriceSentinel,
/// `display_price_pair_unavailable`
#[serde(rename = "display_price_pair_unavailable")]
DisplayPricePairUnavailable,
/// `outcome_identity_unavailable`
#[serde(rename = "outcome_identity_unavailable")]
OutcomeIdentityUnavailable,
/// `final_score_unavailable`
#[serde(rename = "final_score_unavailable")]
FinalScoreUnavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Reason {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::OutcomePricesMissing => "outcome_prices_missing",
Self::OutcomePriceLegMissing => "outcome_price_leg_missing",
Self::ZeroPriceSentinel => "zero_price_sentinel",
Self::DisplayPricePairUnavailable => "display_price_pair_unavailable",
Self::OutcomeIdentityUnavailable => "outcome_identity_unavailable",
Self::FinalScoreUnavailable => "final_score_unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for Reason {
fn from(value: &str) -> Self {
match value {
"outcome_prices_missing" => Self::OutcomePricesMissing,
"outcome_price_leg_missing" => Self::OutcomePriceLegMissing,
"zero_price_sentinel" => Self::ZeroPriceSentinel,
"display_price_pair_unavailable" => Self::DisplayPricePairUnavailable,
"outcome_identity_unavailable" => Self::OutcomeIdentityUnavailable,
"final_score_unavailable" => Self::FinalScoreUnavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Reason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Reason {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RedeliverWebhookDeliveryResponse {
pub object: String,
pub data: WebhookDelivery,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RegisterAgentResponse {
pub object: String,
pub data: AgentRegistration,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReportPayload {
/// Canonical key since #16304; total_whale_trades is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub total_large_trades: Option<i64>,
#[serde(default)]
pub total_whale_trades: Option<i64>,
/// Canonical key since #16304; total_whale_volume is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub total_large_trade_volume: Option<f64>,
#[serde(default)]
pub total_whale_volume: Option<f64>,
#[serde(default)]
pub biggest_trade_size: Option<f64>,
#[serde(default)]
pub active_traders: Option<i64>,
/// Canonical key since #16304; top_whale_trades is its deprecated spelling, emitted beside it with the same value.
#[serde(default)]
pub top_large_trades: Option<Vec<ReportPayloadTopLargeTradesItem>>,
#[serde(default)]
pub top_whale_trades: Option<Vec<ReportPayloadTopWhaleTradesItem>>,
#[serde(default)]
pub categories: Option<Vec<serde_json::Map<String, serde_json::Value>>>,
/// Counts of current grades for distinct Polymarket traders with at least one whale alert in the report's source date range. The grade is the current projection at snapshot materialization time, not a historical grade at trade time. Traders without a current ranking projection are omitted; a null grade entry means the active trader's current grade is unavailable.
#[serde(default)]
pub grade_distribution: Option<Vec<serde_json::Map<String, serde_json::Value>>>,
/// Which markets the report counts. sports_and_esports for a period that ends on or after 2026-10-21: totals, categories, top trades and the grade cohort cover sports and esports markets only. all_markets for an earlier period, whose stored report keeps every Polymarket market it published.
pub market_scope: MarketScope,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReportPayloadTopLargeTradesItem {
/// Provider outcome label for the traded side.
#[serde(default)]
pub outcome: Option<String>,
/// Trade direction (BUY or SELL), not the outcome side.
#[serde(default)]
pub side: Option<String>,
/// Market title.
#[serde(default)]
pub title: Option<String>,
/// Trade size in USD.
#[serde(default)]
pub size: Option<f64>,
/// Trade price in provider [0, 1] units.
#[serde(default)]
pub price: Option<f64>,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the traded outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Whale trade id.
#[serde(default)]
pub id: Option<i64>,
/// Trade timestamp (UTC).
#[serde(default)]
pub trade_time: Option<String>,
/// Provider market category.
#[serde(default)]
pub market_category: Option<String>,
/// Always polymarket, or null when the row carries no stored value.
#[serde(default)]
pub platform: Option<String>,
/// Trader display name, when known.
#[serde(default)]
pub name: Option<String>,
/// Trader pseudonym, when no display name is known.
#[serde(default)]
pub pseudonym: Option<String>,
/// Trader grade (S-F) at snapshot time.
#[serde(default)]
pub trader_grade: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReportPayloadTopWhaleTradesItem {
/// Provider outcome label for the traded side.
#[serde(default)]
pub outcome: Option<String>,
/// Trade direction (BUY or SELL), not the outcome side.
#[serde(default)]
pub side: Option<String>,
/// Market title.
#[serde(default)]
pub title: Option<String>,
/// Trade size in USD.
#[serde(default)]
pub size: Option<f64>,
/// Trade price in provider [0, 1] units.
#[serde(default)]
pub price: Option<f64>,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the traded outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Whale trade id.
#[serde(default)]
pub id: Option<i64>,
/// Trade timestamp (UTC).
#[serde(default)]
pub trade_time: Option<String>,
/// Provider market category.
#[serde(default)]
pub market_category: Option<String>,
/// Always polymarket, or null when the row carries no stored value.
#[serde(default)]
pub platform: Option<String>,
/// Trader display name, when known.
#[serde(default)]
pub name: Option<String>,
/// Trader pseudonym, when no display name is known.
#[serde(default)]
pub pseudonym: Option<String>,
/// Trader grade (S-F) at snapshot time.
#[serde(default)]
pub trader_grade: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReportReconciliation {
pub volume_kind: String,
/// Where the whale volume in this report is read from: the column name `whale_alerts.usdc_notional_num`, followed in parentheses by the reconciliation note the server attaches to it. The server has always sent the note with the name, so this is not a bare constant; match on the prefix, not the whole string.
pub whale_volume_source: String,
pub notes: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReportSnapshot {
pub kind: Granularity,
pub generated_at: String,
pub source_range: ReportSourceRange,
pub snapshot: SnapshotState,
pub completeness: SnapshotCompleteness,
pub reconciliation: ReportReconciliation,
pub report: ReportPayload,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ReportSourceRange {
pub start_date: String,
pub end_date: String,
pub timezone: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum RequiredTier {
/// `account`
#[serde(rename = "account")]
Account,
/// `insider`
#[serde(rename = "insider")]
Insider,
/// `max`
#[serde(rename = "max")]
Max,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl RequiredTier {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Account => "account",
Self::Insider => "insider",
Self::Max => "max",
Self::Other(value) => value,
}
}
}
impl From<&str> for RequiredTier {
fn from(value: &str) -> Self {
match value {
"account" => Self::Account,
"insider" => Self::Insider,
"max" => Self::Max,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for RequiredTier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for RequiredTier {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Resolution {
/// `1d`
#[serde(rename = "1d")]
V1d,
/// `1w`
#[serde(rename = "1w")]
V1w,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Resolution {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::V1d => "1d",
Self::V1w => "1w",
Self::Other(value) => value,
}
}
}
impl From<&str> for Resolution {
fn from(value: &str) -> Self {
match value {
"1d" => Self::V1d,
"1w" => Self::V1w,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Resolution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Resolution {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ResponseMeta {
/// Unique request ID (req_ prefix). The same value as the X-Request-Id response header, the request's usage accounting row and its log lines.
pub request_id: String,
pub cached: bool,
/// Cache age in seconds. Omitted when the response was not cached, and also when it was cached but its age cannot be established (an entry stored before its cache carried a computed instant). Never a placeholder: an unknown age is reported as no value rather than as the cache TTL.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_age_s: Option<i64>,
/// Advisory request weight (relative compute cost). 1 for simple reads; higher for heavier endpoints. Not a credit/price.
pub cost: i64,
/// Committed PostgreSQL-owned leaderboard generation for the returned rows and cursor. Present on GET /api/v1/leaderboard; omitted on endpoints that do not read this ranking.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ranking_generation: Option<i64>,
/// Authoritative RFC3339 timestamp from cache_generations.updated_at for ranking_generation. It is read in the same repeatable-read snapshot as the leaderboard rows and is not request time, cache write time, or row insertion order.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ranking_as_of: Option<String>,
/// Directional evidence provenance for Edge Hunter signals collections. The default board carries this provenance in data.directional_source and omits this optional meta field.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub directional_source: Option<DirectionalSource>,
/// Ranking evidence provenance for Edge Hunter signals collections. The default board carries this clock/provenance in data.ranking_source and omits this optional meta field.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ranking_source: Option<RankingSource>,
/// Whole filtered snapshot category-evidence status before pagination. Operational live always remains partial source coverage.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_source: Option<CategorySkillSource>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_model_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_taxonomy_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_platform: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_scope: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_source_coverage: Option<SourceCoverage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_observation_started_at: Option<String>,
/// Whole-model operational readiness captured with the category model snapshot. Present on category-enriched responses even when the filtered signal list is empty. When true, category_skill_source is degraded and Edge Hunter signals uses the shorter degraded cache TTL.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_model_operationally_degraded: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_status_counts: Option<ResponseMetaCategorySkillStatusCounts>,
/// SHA-256 of the funded signal membership/order/rank/cursor vector immediately before category-skill enrichment. Edge Hunter signals view only.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_base_payload_hash: Option<String>,
/// Independent SHA-256 recomputation over the same base fields immediately after category-skill enrichment. Equality with category_skill_base_payload_hash proves category-skill enrichment did not change funded inputs. Edge Hunter signals view only.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_enriched_base_payload_hash: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ResponseMetaCategorySkillStatusCounts {
pub live: i64,
pub insufficient: i64,
pub stale: i64,
pub unknown: i64,
pub degraded: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RetireWebhookSecretResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum RetryScheduleReason {
/// `receiver_retry_after`
#[serde(rename = "receiver_retry_after")]
ReceiverRetryAfter,
/// `transient_failure`
#[serde(rename = "transient_failure")]
TransientFailure,
/// `permanent_or_auth_failure`
#[serde(rename = "permanent_or_auth_failure")]
PermanentOrAuthFailure,
/// `manual_redelivery`
#[serde(rename = "manual_redelivery")]
ManualRedelivery,
/// `configuration_changed`
#[serde(rename = "configuration_changed")]
ConfigurationChanged,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl RetryScheduleReason {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::ReceiverRetryAfter => "receiver_retry_after",
Self::TransientFailure => "transient_failure",
Self::PermanentOrAuthFailure => "permanent_or_auth_failure",
Self::ManualRedelivery => "manual_redelivery",
Self::ConfigurationChanged => "configuration_changed",
Self::Other(value) => value,
}
}
}
impl From<&str> for RetryScheduleReason {
fn from(value: &str) -> Self {
match value {
"receiver_retry_after" => Self::ReceiverRetryAfter,
"transient_failure" => Self::TransientFailure,
"permanent_or_auth_failure" => Self::PermanentOrAuthFailure,
"manual_redelivery" => Self::ManualRedelivery,
"configuration_changed" => Self::ConfigurationChanged,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for RetryScheduleReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for RetryScheduleReason {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct RotateWebhookSecretResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
/// An entitled same-day slot with a future scheduled release: stable identity and backend-owned release/kickoff instants. It leaves this list at its release instant; an unpublished slot is never a delayed recommendation. No matchup, category, platform, side, price, or holder fields appear before release. Unauthorized scheduled ranks appear only in locked_picks.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ScheduledPickSlot {
/// Deprecated compatibility daily release slot; use pick_id for identity and publication_order for scheduling.
pub pick_rank: i64,
/// The slot's future release instant. Remove this preview at that instant and read again for published picks; a scheduled slot does not guarantee publication.
pub release_at: String,
/// The backed game's current kickoff instant.
pub kickoff: String,
/// Stable pick row identity as decimal text. Never use a quality rank as identity.
pub pick_id: String,
/// Compatibility release slot. No quality claim; historic scheduling order is retained.
pub publication_order: i64,
/// Viewer-independent free selection designation. New rows store it explicitly; historic null storage uses the original free slot.
pub is_free_selection: bool,
/// Deprecated (#24157): always null. No replacement lineage was ever recorded in this field; the key stays so existing V1 clients keep parsing. Identify a pick by pick_id.
#[serde(default)]
pub supersedes_pick_id: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SchemaVersion {
/// `trader-export-json-v1`
#[serde(rename = "trader-export-json-v1")]
TraderExportJsonV1,
/// `trader-export-ndjson-v1`
#[serde(rename = "trader-export-ndjson-v1")]
TraderExportNdjsonV1,
/// `trader-export-csv-v1`
#[serde(rename = "trader-export-csv-v1")]
TraderExportCsvV1,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SchemaVersion {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::TraderExportJsonV1 => "trader-export-json-v1",
Self::TraderExportNdjsonV1 => "trader-export-ndjson-v1",
Self::TraderExportCsvV1 => "trader-export-csv-v1",
Self::Other(value) => value,
}
}
}
impl From<&str> for SchemaVersion {
fn from(value: &str) -> Self {
match value {
"trader-export-json-v1" => Self::TraderExportJsonV1,
"trader-export-ndjson-v1" => Self::TraderExportNdjsonV1,
"trader-export-csv-v1" => Self::TraderExportCsvV1,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SchemaVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SchemaVersion {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// One side's score in a single set. The verbatim provider text stays on the team's `score` string; this is the parsed form.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ScoreCell {
/// Games won in this set.
pub games: i64,
/// Tiebreak points won in this set. Omitted when the set had no tiebreak; absence and zero are different.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tiebreak: Option<i64>,
}
/// Shape the provider score string was parsed into. `two_side` is one aggregate per side (basketball `105-98`), `multi_set` is per-set columns (tennis `6-7(5-7), 6-0, 1-0`), `esports_series` is a maps/sets/format triplet (`000-000|2-0|Bo3`). Omitted when the score could not be parsed.
///
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ScoreFormat {
/// `two_side`
#[serde(rename = "two_side")]
TwoSide,
/// `multi_set`
#[serde(rename = "multi_set")]
MultiSet,
/// `esports_series`
#[serde(rename = "esports_series")]
EsportsSeries,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ScoreFormat {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::TwoSide => "two_side",
Self::MultiSet => "multi_set",
Self::EsportsSeries => "esports_series",
Self::Other(value) => value,
}
}
}
impl From<&str> for ScoreFormat {
fn from(value: &str) -> Self {
match value {
"two_side" => Self::TwoSide,
"multi_set" => Self::MultiSet,
"esports_series" => Self::EsportsSeries,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ScoreFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ScoreFormat {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SearchContentResponse {
pub object: String,
pub data: Vec<ContentSearchResult>,
pub has_more: bool,
/// Omitted because content search is a bounded one-page result.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Absent because content search is a bounded relevance result and does not run a count query.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SearchMarketsResponse {
pub object: String,
pub data: Vec<MarketSearchResult>,
pub has_more: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
/// Total matching rows when the read model exposes a count; the key is absent when it does not.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<i64>,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SearchMarketsStatus {
/// `active`
#[serde(rename = "active")]
Active,
/// `closed`
#[serde(rename = "closed")]
Closed,
/// `all`
#[serde(rename = "all")]
All,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SearchMarketsStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Active => "active",
Self::Closed => "closed",
Self::All => "all",
Self::Other(value) => value,
}
}
}
impl From<&str> for SearchMarketsStatus {
fn from(value: &str) -> Self {
match value {
"active" => Self::Active,
"closed" => Self::Closed,
"all" => Self::All,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SearchMarketsStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SearchMarketsStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Sections {
/// `entries`
#[serde(rename = "entries")]
Entries,
/// `stats`
#[serde(rename = "stats")]
Stats,
/// `monthly`
#[serde(rename = "monthly")]
Monthly,
/// `year_totals`
#[serde(rename = "year_totals")]
YearTotals,
/// `drawdown`
#[serde(rename = "drawdown")]
Drawdown,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Sections {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Entries => "entries",
Self::Stats => "stats",
Self::Monthly => "monthly",
Self::YearTotals => "year_totals",
Self::Drawdown => "drawdown",
Self::Other(value) => value,
}
}
}
impl From<&str> for Sections {
fn from(value: &str) -> Self {
match value {
"entries" => Self::Entries,
"stats" => Self::Stats,
"monthly" => Self::Monthly,
"year_totals" => Self::YearTotals,
"drawdown" => Self::Drawdown,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Sections {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Sections {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ServingSide {
/// `first`
#[serde(rename = "first")]
First,
/// `second`
#[serde(rename = "second")]
Second,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl ServingSide {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::First => "first",
Self::Second => "second",
Self::Other(value) => value,
}
}
}
impl From<&str> for ServingSide {
fn from(value: &str) -> Self {
match value {
"first" => Self::First,
"second" => Self::Second,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for ServingSide {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for ServingSide {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Severity {
/// `flag`
#[serde(rename = "flag")]
Flag,
/// `watch`
#[serde(rename = "watch")]
Watch,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Severity {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Flag => "flag",
Self::Watch => "watch",
Self::Other(value) => value,
}
}
}
impl From<&str> for Severity {
fn from(value: &str) -> Self {
match value {
"flag" => Self::Flag,
"watch" => Self::Watch,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Severity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Severity {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// `sharp_money_flow_detected` data (Pro and Max): ranked-trader net flow on a market crossed the threshold. The deprecated spelling `smart_money_flow_detected` delivers the same object.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SharpMoneyFlowDetectedData {
/// Polymarket condition id of the market.
pub condition_id: String,
/// Signed ranked-trader net flow in USD over `window`: positive is net into YES, negative net into NO.
pub net_flow_usd: f64,
/// Absolute value of `net_flow_usd`, the magnitude compared with the threshold.
pub abs_net_flow_usd: f64,
/// `yes` when `net_flow_usd` is 0 or more, else `no`.
pub dominant_side: Side,
/// Minimum trader grade included in the flow.
pub grade_floor: Grade,
/// Ranked-trader large fills that make up the flow.
pub whale_trade_count: i64,
/// Window the flow was aggregated over, for example `24h`.
pub window: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Side {
/// `yes`
#[serde(rename = "yes")]
Yes,
/// `no`
#[serde(rename = "no")]
No,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Side {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Yes => "yes",
Self::No => "no",
Self::Other(value) => value,
}
}
}
impl From<&str> for Side {
fn from(value: &str) -> Self {
match value {
"yes" => Self::Yes,
"no" => Self::No,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Side {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Side {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SmartMoneyFlowMarket {
pub market: SmartMoneyFlowMarketMarket,
/// Sharp-money flow aggregate for the market (canonical; smart_money is a deprecated byte-identical alias).
pub sharp_money: SmartMoneyFlowMarketSharpMoney,
/// Deprecated alias of sharp_money; byte-identical and retained for backward compatibility.
pub smart_money: SmartMoneyFlowMarketSmartMoney,
pub timeframe: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SmartMoneyFlowMarketMarket {
pub id: String,
pub condition_id: String,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub slug: Option<String>,
#[serde(default)]
pub category: Option<String>,
/// Provider platform. Always polymarket, or null when the row carries no stored value.
#[serde(default)]
pub platform: Option<String>,
}
/// Sharp-money flow aggregate for the market (canonical; smart_money is a deprecated byte-identical alias).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SmartMoneyFlowMarketSharpMoney {
pub net_flow_usd: f64,
pub direction: Direction,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the net-flow direction outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Canonical key since #16304 (Polymarket's noun is large trade); whale_trade_count is its deprecated spelling, emitted beside it with the same value.
pub large_trade_count: i64,
pub whale_trade_count: i64,
pub buy_volume_usd: f64,
pub sell_volume_usd: f64,
}
/// Deprecated alias of sharp_money; byte-identical and retained for backward compatibility.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SmartMoneyFlowMarketSmartMoney {
pub net_flow_usd: f64,
pub direction: Direction,
/// The Polymarket CLOB token id (ERC1155 asset id, decimal string) for the net-flow direction outcome; null when unavailable (e.g. unsynced markets).
#[serde(default)]
pub token_id: Option<String>,
/// Canonical key since #16304 (Polymarket's noun is large trade); whale_trade_count is its deprecated spelling, emitted beside it with the same value.
pub large_trade_count: i64,
pub whale_trade_count: i64,
pub buy_volume_usd: f64,
pub sell_volume_usd: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SnapshotCompleteness {
/// complete only on a final body (snapshot.status final). partial for a live range and for a closed range whose body was read before final_after; reason says which. empty when the range has no whale activity.
pub status: SnapshotCompletenessStatus,
pub reason: String,
pub expected_days: i64,
/// Canonical key since #16304; covered_days_with_whale_activity is its deprecated spelling, emitted beside it with the same value.
pub covered_days_with_large_trade_activity: i64,
pub covered_days_with_whale_activity: i64,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SnapshotCompletenessStatus {
/// `complete`
#[serde(rename = "complete")]
Complete,
/// `partial`
#[serde(rename = "partial")]
Partial,
/// `empty`
#[serde(rename = "empty")]
Empty,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SnapshotCompletenessStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Complete => "complete",
Self::Partial => "partial",
Self::Empty => "empty",
Self::Other(value) => value,
}
}
}
impl From<&str> for SnapshotCompletenessStatus {
fn from(value: &str) -> Self {
match value {
"complete" => Self::Complete,
"partial" => Self::Partial,
"empty" => Self::Empty,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SnapshotCompletenessStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SnapshotCompletenessStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SnapshotState {
/// Immutable content version for a durable report identity; 0 for an ephemeral explicit weekly from/to range.
pub version: i64,
/// durable for canonical daily, ISO-week, and monthly snapshot identities; ephemeral for explicit weekly from/to ranges, which are recomputed within a bounded 31-day window and never persisted.
pub storage: Storage,
/// final only for a body whose source read started at or after final_after (the UTC close of the range plus the whale-trade ingestion budget). rolling for a live range and for a closed range still inside that budget: such a body refreshes every five minutes, is rebuilt once after final_after, and is then frozen. Before 2026-09-22 a closed range read final from the calendar alone, even for a body built before the range ended (#16225).
pub status: SnapshotStateStatus,
pub generated_at: String,
/// For a rolling body, the UTC date on which a final body can first be built (the date of final_after); null once final.
#[serde(default)]
pub mutable_until: Option<String>,
/// Whether the source range has ended on the UTC calendar. Closing is not finality: a closed range is rolling until final_after.
pub period_closed: bool,
/// The instant a source read must start at or after to produce a final body: the range's UTC close plus 7,530 s, the whale-trade ingestion budget (p95 block lag gate plus the projection retry budget).
pub final_after: String,
/// When this body's source read started, the clock finality is judged on. null for a body stored before that clock was recorded.
#[serde(default)]
pub source_read_started_at: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SnapshotStateStatus {
/// `final`
#[serde(rename = "final")]
Final,
/// `rolling`
#[serde(rename = "rolling")]
Rolling,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SnapshotStateStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Final => "final",
Self::Rolling => "rolling",
Self::Other(value) => value,
}
}
}
impl From<&str> for SnapshotStateStatus {
fn from(value: &str) -> Self {
match value {
"final" => Self::Final,
"rolling" => Self::Rolling,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SnapshotStateStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SnapshotStateStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Sort {
/// `recent`
#[serde(rename = "recent")]
Recent,
/// `market_volume_share`
#[serde(rename = "market_volume_share")]
MarketVolumeShare,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Sort {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Recent => "recent",
Self::MarketVolumeShare => "market_volume_share",
Self::Other(value) => value,
}
}
}
impl From<&str> for Sort {
fn from(value: &str) -> Self {
match value {
"recent" => Self::Recent,
"market_volume_share" => Self::MarketVolumeShare,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Sort {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Sort {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Source {
/// `live`
#[serde(rename = "live")]
Live,
/// `upcoming`
#[serde(rename = "upcoming")]
Upcoming,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Source {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Live => "live",
Self::Upcoming => "upcoming",
Self::Other(value) => value,
}
}
}
impl From<&str> for Source {
fn from(value: &str) -> Self {
match value {
"live" => Self::Live,
"upcoming" => Self::Upcoming,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Source {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Source {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SourceAvailability {
/// `available`
#[serde(rename = "available")]
Available,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// `not_applicable`
#[serde(rename = "not_applicable")]
NotApplicable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SourceAvailability {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Available => "available",
Self::Unavailable => "unavailable",
Self::NotApplicable => "not_applicable",
Self::Other(value) => value,
}
}
}
impl From<&str> for SourceAvailability {
fn from(value: &str) -> Self {
match value {
"available" => Self::Available,
"unavailable" => Self::Unavailable,
"not_applicable" => Self::NotApplicable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SourceAvailability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SourceAvailability {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SourceCoverage {
/// `partial_whale_threshold_fills`
#[serde(rename = "partial_whale_threshold_fills")]
PartialWhaleThresholdFills,
/// `graded_wallet_fills`
#[serde(rename = "graded_wallet_fills")]
GradedWalletFills,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SourceCoverage {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::PartialWhaleThresholdFills => "partial_whale_threshold_fills",
Self::GradedWalletFills => "graded_wallet_fills",
Self::Other(value) => value,
}
}
}
impl From<&str> for SourceCoverage {
fn from(value: &str) -> Self {
match value {
"partial_whale_threshold_fills" => Self::PartialWhaleThresholdFills,
"graded_wallet_fills" => Self::GradedWalletFills,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SourceCoverage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SourceCoverage {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SourceFreshness {
/// `fresh`
#[serde(rename = "fresh")]
Fresh,
/// `stale`
#[serde(rename = "stale")]
Stale,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// `not_applicable`
#[serde(rename = "not_applicable")]
NotApplicable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SourceFreshness {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Fresh => "fresh",
Self::Stale => "stale",
Self::Unknown => "unknown",
Self::NotApplicable => "not_applicable",
Self::Other(value) => value,
}
}
}
impl From<&str> for SourceFreshness {
fn from(value: &str) -> Self {
match value {
"fresh" => Self::Fresh,
"stale" => Self::Stale,
"unknown" => Self::Unknown,
"not_applicable" => Self::NotApplicable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SourceFreshness {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SourceFreshness {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SourceStatus {
/// `ok`
#[serde(rename = "ok")]
Ok,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SourceStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Ok => "ok",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for SourceStatus {
fn from(value: &str) -> Self {
match value {
"ok" => Self::Ok,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SourceStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SourceStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum State {
/// `scheduled`
#[serde(rename = "scheduled")]
Scheduled,
/// `live`
#[serde(rename = "live")]
Live,
/// `paused`
#[serde(rename = "paused")]
Paused,
/// `ended`
#[serde(rename = "ended")]
Ended,
/// `postponed`
#[serde(rename = "postponed")]
Postponed,
/// `cancelled`
#[serde(rename = "cancelled")]
Cancelled,
/// `suspended`
#[serde(rename = "suspended")]
Suspended,
/// `delayed`
#[serde(rename = "delayed")]
Delayed,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl State {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Scheduled => "scheduled",
Self::Live => "live",
Self::Paused => "paused",
Self::Ended => "ended",
Self::Postponed => "postponed",
Self::Cancelled => "cancelled",
Self::Suspended => "suspended",
Self::Delayed => "delayed",
Self::Unknown => "unknown",
Self::Other(value) => value,
}
}
}
impl From<&str> for State {
fn from(value: &str) -> Self {
match value {
"scheduled" => Self::Scheduled,
"live" => Self::Live,
"paused" => Self::Paused,
"ended" => Self::Ended,
"postponed" => Self::Postponed,
"cancelled" => Self::Cancelled,
"suspended" => Self::Suspended,
"delayed" => Self::Delayed,
"unknown" => Self::Unknown,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for State {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for State {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Status {
/// `live`
#[serde(rename = "live")]
Live,
/// `insufficient`
#[serde(rename = "insufficient")]
Insufficient,
/// `stale`
#[serde(rename = "stale")]
Stale,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// `degraded`
#[serde(rename = "degraded")]
Degraded,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Status {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Live => "live",
Self::Insufficient => "insufficient",
Self::Stale => "stale",
Self::Unknown => "unknown",
Self::Degraded => "degraded",
Self::Other(value) => value,
}
}
}
impl From<&str> for Status {
fn from(value: &str) -> Self {
match value {
"live" => Self::Live,
"insufficient" => Self::Insufficient,
"stale" => Self::Stale,
"unknown" => Self::Unknown,
"degraded" => Self::Degraded,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Status {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Storage {
/// `durable`
#[serde(rename = "durable")]
Durable,
/// `ephemeral`
#[serde(rename = "ephemeral")]
Ephemeral,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Storage {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Durable => "durable",
Self::Ephemeral => "ephemeral",
Self::Other(value) => value,
}
}
}
impl From<&str> for Storage {
fn from(value: &str) -> Self {
match value {
"durable" => Self::Durable,
"ephemeral" => Self::Ephemeral,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Storage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Storage {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Strategy {
/// `accumulator`
#[serde(rename = "accumulator")]
Accumulator,
/// `algo_trader`
#[serde(rename = "algo_trader")]
AlgoTrader,
/// `arbitrageur`
#[serde(rename = "arbitrageur")]
Arbitrageur,
/// `category_focused`
#[serde(rename = "category_focused")]
CategoryFocused,
/// `directional`
#[serde(rename = "directional")]
Directional,
/// `diversified`
#[serde(rename = "diversified")]
Diversified,
/// `event_driven`
#[serde(rename = "event_driven")]
EventDriven,
/// `high_activity`
#[serde(rename = "high_activity")]
HighActivity,
/// `market_maker`
#[serde(rename = "market_maker")]
MarketMaker,
/// `mixed`
#[serde(rename = "mixed")]
Mixed,
/// `momentum`
#[serde(rename = "momentum")]
Momentum,
/// `scalper`
#[serde(rename = "scalper")]
Scalper,
/// `speculator`
#[serde(rename = "speculator")]
Speculator,
/// `swing_trader`
#[serde(rename = "swing_trader")]
SwingTrader,
/// `two_sided`
#[serde(rename = "two_sided")]
TwoSided,
/// `unclassified`
#[serde(rename = "unclassified")]
Unclassified,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Strategy {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Accumulator => "accumulator",
Self::AlgoTrader => "algo_trader",
Self::Arbitrageur => "arbitrageur",
Self::CategoryFocused => "category_focused",
Self::Directional => "directional",
Self::Diversified => "diversified",
Self::EventDriven => "event_driven",
Self::HighActivity => "high_activity",
Self::MarketMaker => "market_maker",
Self::Mixed => "mixed",
Self::Momentum => "momentum",
Self::Scalper => "scalper",
Self::Speculator => "speculator",
Self::SwingTrader => "swing_trader",
Self::TwoSided => "two_sided",
Self::Unclassified => "unclassified",
Self::Other(value) => value,
}
}
}
impl From<&str> for Strategy {
fn from(value: &str) -> Self {
match value {
"accumulator" => Self::Accumulator,
"algo_trader" => Self::AlgoTrader,
"arbitrageur" => Self::Arbitrageur,
"category_focused" => Self::CategoryFocused,
"directional" => Self::Directional,
"diversified" => Self::Diversified,
"event_driven" => Self::EventDriven,
"high_activity" => Self::HighActivity,
"market_maker" => Self::MarketMaker,
"mixed" => Self::Mixed,
"momentum" => Self::Momentum,
"scalper" => Self::Scalper,
"speculator" => Self::Speculator,
"swing_trader" => Self::SwingTrader,
"two_sided" => Self::TwoSided,
"unclassified" => Self::Unclassified,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Strategy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Strategy {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum StreakTier {
/// `hot`
#[serde(rename = "hot")]
Hot,
/// `rising`
#[serde(rename = "rising")]
Rising,
/// `neutral`
#[serde(rename = "neutral")]
Neutral,
/// `cooling`
#[serde(rename = "cooling")]
Cooling,
/// `cold`
#[serde(rename = "cold")]
Cold,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl StreakTier {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Hot => "hot",
Self::Rising => "rising",
Self::Neutral => "neutral",
Self::Cooling => "cooling",
Self::Cold => "cold",
Self::Other(value) => value,
}
}
}
impl From<&str> for StreakTier {
fn from(value: &str) -> Self {
match value {
"hot" => Self::Hot,
"rising" => Self::Rising,
"neutral" => Self::Neutral,
"cooling" => Self::Cooling,
"cold" => Self::Cold,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for StreakTier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for StreakTier {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SubmitWhaleDatasetBody {
pub from: String,
pub to: String,
/// Raw provider condition id or mkt_-prefixed id.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition_id: Option<String>,
/// USD minimum, normalized to cents like replay.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min_size: Option<f64>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SuspicionTrack {
/// `whale`
#[serde(rename = "whale")]
Whale,
/// `fresh_conviction`
#[serde(rename = "fresh_conviction")]
FreshConviction,
/// `sliced_position`
#[serde(rename = "sliced_position")]
SlicedPosition,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl SuspicionTrack {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Whale => "whale",
Self::FreshConviction => "fresh_conviction",
Self::SlicedPosition => "sliced_position",
Self::Other(value) => value,
}
}
}
impl From<&str> for SuspicionTrack {
fn from(value: &str) -> Self {
match value {
"whale" => Self::Whale,
"fresh_conviction" => Self::FreshConviction,
"sliced_position" => Self::SlicedPosition,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for SuspicionTrack {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for SuspicionTrack {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SuspiciousTrade {
/// Prefixed ID (rf_...).
pub id: String,
/// Stored score that met the live flag threshold.
pub suspicion_score: f64,
/// The live scorer persists one threshold class.
pub severity: String,
pub trader: SuspiciousTradeTrader,
pub market: SuspiciousTradeMarket,
pub scores: SuspiciousTradeScores,
/// Stored whale_alerts.suspicion_signals JSON from the scorer.
pub evidence: serde_json::Value,
/// Stored trade timestamp.
pub created_at: String,
}
/// `suspicious_trade_flagged` data (Pro and Max): a trade's suspicion score crossed the flag threshold for the first time. The deprecated spelling `insider_radar_flag_raised` delivers the same object.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SuspiciousTradeFlaggedData {
/// Numeric id of the flagged trade, a JSON number. It is the `id` that `GET /api/v1/large-trades/{id}` accepts.
pub trade_id: i64,
/// Address of the wallet that made the trade.
pub wallet: String,
/// 0xinsider's numeric id for the wallet.
pub trader_id: i64,
/// Polymarket condition id of the market.
pub condition_id: String,
/// The suspicion score, 0 to 100, that crossed the threshold.
pub suspicion_score: f64,
/// The scoring track that raised the flag.
pub track: SuspicionTrack,
/// Outcome side the flagged fill took.
pub side: Side,
/// Share count of the flagged fill.
pub size: f64,
/// Fill price of the flagged trade.
pub price: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SuspiciousTradeMarket {
pub id: String,
pub condition_id: String,
/// Canonical market question when available.
#[serde(default)]
pub title: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SuspiciousTradeScores {
/// Null because the live scorer does not record this component.
#[serde(default)]
pub timing: Option<f64>,
/// Null because the live scorer does not record this component.
#[serde(default)]
pub edge: Option<f64>,
/// Numeric evidence.size when recorded; otherwise null.
#[serde(default)]
pub size: Option<f64>,
/// Numeric evidence.fresh when recorded; otherwise null.
#[serde(default)]
pub fresh_wallet: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SuspiciousTradeTrader {
pub id: String,
pub address: String,
#[serde(default)]
pub username: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Sweep {
/// `win`
#[serde(rename = "win")]
Win,
/// `loss`
#[serde(rename = "loss")]
Loss,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Sweep {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Win => "win",
Self::Loss => "loss",
Self::Other(value) => value,
}
}
}
impl From<&str> for Sweep {
fn from(value: &str) -> Self {
match value {
"win" => Self::Win,
"loss" => Self::Loss,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Sweep {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Sweep {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Optional current-game tennis facts from API-Tennis, aligned with live_score.scores display order. These facts have their own revision and expiry; Polymarket remains the source of sets, match status, period, and the outer live-score revision. New observations require corroborated identity, current set/games, and live state. Cached responses and replayed frames may still carry expired facts, so clients must enforce expires_at.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TennisPoints {
pub source: String,
/// API-Tennis match ID. This is not a Polymarket market or event ID.
pub match_key: i64,
/// API-Tennis player IDs in the same first/second order as live_score.scores. These are not Polymarket team IDs.
pub player_keys: Vec<i64>,
/// Current-game point values in first/second order. Read strings, including A for advantage and numeric tiebreak points. Missing points are not zero.
pub points: Vec<String>,
/// The serving player in live_score.scores display order. Omitted when API-Tennis does not identify the server.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub serving_side: Option<ServingSide>,
/// Orders only the tennis_points group for the same match. Never compare this with live_score.source_revision.
pub source_revision: i64,
/// When 0xinsider observed these API-Tennis facts. This is not a timestamp from the court.
pub observed_at: String,
/// Stop displaying the point group at this instant, even if the enclosing score remains fresh. The group expires 30 seconds after observed_at.
pub expires_at: String,
/// Current set number. Display points only while this matches the enclosing scoreboard.
pub set_number: i64,
/// Current-set games in first/second order. Display points only while these match the enclosing scoreboard.
pub games: Vec<i64>,
}
/// A provider-reported ATP/WTA singles rank with independent retrieval and expiry clocks. API-Tennis provides no ranking publication date. Retrieval time does not establish when the tour published the ranking.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TennisRanking {
/// Provider-reported singles rank.
pub rank: i64,
/// The player ranking tour.
pub tour: TennisRankingTour,
/// The standings provider.
pub source: String,
/// Successful snapshot retrieval time in UTC, not ranking publication date.
pub observed_at: String,
/// UTC deadline after which clients must hide this rank, including when an older game or pick response remains cached.
pub expires_at: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TennisRankingTour {
/// `atp`
#[serde(rename = "atp")]
Atp,
/// `wta`
#[serde(rename = "wta")]
Wta,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl TennisRankingTour {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Atp => "atp",
Self::Wta => "wta",
Self::Other(value) => value,
}
}
}
impl From<&str> for TennisRankingTour {
fn from(value: &str) -> Self {
match value {
"atp" => Self::Atp,
"wta" => Self::Wta,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for TennisRankingTour {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for TennisRankingTour {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Timeframe {
/// `1h`
#[serde(rename = "1h")]
V1h,
/// `4h`
#[serde(rename = "4h")]
V4h,
/// `24h`
#[serde(rename = "24h")]
V24h,
/// `7d`
#[serde(rename = "7d")]
V7d,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Timeframe {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::V1h => "1h",
Self::V4h => "4h",
Self::V24h => "24h",
Self::V7d => "7d",
Self::Other(value) => value,
}
}
}
impl From<&str> for Timeframe {
fn from(value: &str) -> Self {
match value {
"1h" => Self::V1h,
"4h" => Self::V4h,
"24h" => Self::V24h,
"7d" => Self::V7d,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Timeframe {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Timeframe {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Tour {
/// `atp`
#[serde(rename = "atp")]
Atp,
/// `wta`
#[serde(rename = "wta")]
Wta,
/// `itf`
#[serde(rename = "itf")]
Itf,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Tour {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Atp => "atp",
Self::Wta => "wta",
Self::Itf => "itf",
Self::Other(value) => value,
}
}
}
impl From<&str> for Tour {
fn from(value: &str) -> Self {
match value {
"atp" => Self::Atp,
"wta" => Self::Wta,
"itf" => Self::Itf,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Tour {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Tour {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Trader {
/// Prefixed ID (trd_...).
pub id: String,
pub address: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grade: Option<Grade>,
/// Hot-streak tier (trailing-7d cross-sectional percentile); a separate axis from the all-time grade. Omitted when there is no recent activity.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub streak_tier: Option<StreakTier>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub score: Option<f64>,
/// Optional forecast score on the documented display scale. Null when unavailable.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub forecast_score: Option<f64>,
/// Optional measured forecast context. Missing values remain unavailable rather than being inferred.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub forecast_evidence: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rank: Option<i64>,
pub pnl: TraderPnlSummary,
pub stats: TraderStats,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub strategy: Option<TraderStrategy>,
/// Per-category rank context when expand=categories is requested. Values include available rank, category totals, performance and record counts; scaled_total_pnl is a legacy alias of total_pnl. Its measurement basis and update timing differ from the plain record returned by GET /api/v1/trader/{address}/categories, so the two need not agree. Use this for rank context and that route for the plain record. The inner key-set is intentionally unconstrained and may contain additional compatibility fields.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_strengths: Option<serde_json::Map<String, serde_json::Value>>,
/// Curated advanced risk/performance metrics (expand=quant_metrics or expand[]=quant_metrics). Omitted unless expanded and backed by a computed row strictly under six hours old; a missing row, NULL computed_at, or age of exactly six hours or more is stale and omitted. Provider-input changes may intentionally lag inside the bounded six-hour window. When present, all listed fields are present (each is a number or null); null means insufficient trade history and must not be treated as 0. The fixed field shape is unchanged.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub quant_metrics: Option<TraderQuantMetrics>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_active: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub synced_at: Option<String>,
/// synced, unknown, or pending.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sync_status: Option<String>,
/// Data age and coverage for this trader body. Always present. Its six groups are sync (traders.last_synced, covering pnl.net_total, pnl.realized, stats.markets_traded, stats.win_rate, stats.daily_win_rate, last_active, synced_at and sync_status), ranking (trader_rankings.computed_at, covering grade, score, streak_tier, forecast_score and forecast_evidence), leaderboard_rank (leaderboard_rank_refresh_state.completed_at, the completion time of the latest fully completed global rank refresh, covering rank), volume (trader_usd_volume.observed_at, covering stats.total_volume) and positions (trader_position_snapshots.last_refreshed_at with traders.last_synced as fallback, covering pnl.unrealized, the open-position aggregate) and leaderboard_pnl (trader_profile_provider_snapshots.observed_at, covering pnl.total: unavailable when Polymarket's all-time leaderboard has no current observation ranking the wallet). The positions clock is the latest successful /positions snapshot when one exists, otherwise the last completed trader sync; it does not date closed or native accounting values. A rank or position value remains unknown or unavailable when its clock or value is absent. For an unknown wallet every group is unavailable. If the open-position read itself fails, positions is unavailable with a reason that says so, pnl.unrealized is absent, and the body is answered fresh (meta.cached false) and is not kept for later callers.
pub data_quality: DataQuality,
/// Field-level trust metadata. Present only when expand=trust or expand[]=trust is requested.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trust: Option<TraderTrust>,
/// Current evidence for observed and known categories (expand=categories or expand[]=categories). Omitted unless expanded. Includes insufficient, stale, unknown and degraded rows; absence of a category is not proof of skill. The global grade is unchanged.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_records: Option<Vec<CategorySkillV2>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category_skill_model: Option<CategorySkillModelReadiness>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderCategoryRecord {
/// Canonical category bucket, for example Soccer, Esports, Tennis. Esports is one bucket, and the Esports record carries its per-game records in games.
pub category: String,
/// Markets in this category the wallet closed with a profit.
pub wins: i64,
/// Markets it closed with a profit or a loss. A market that resolved at exactly zero P&L is in neither count, so this is not the wallet's market count in the category.
pub decided: i64,
/// wins / decided in [0,1], truncated to four decimals. Null when status is not_enough_data.
#[serde(default)]
pub win_rate: Option<f64>,
/// measured: decided cleared min_decided_for_win_rate and win_rate carries the rate. not_enough_data: the counts are real but the sample is under the floor, so no percent is published. A failed read is an error response, never a status value.
pub status: TraderEsportsGameRecordStatus,
/// The wallet's record per esports title, busiest first. Present only on the Esports record, and only when the wallet has a settled market in at least one title; absent on every other category and on an Esports record whose markets are all in series that are not games (skin-price indices, streamer props) or carry no series. Absent means no per-game record, the same rule as an absent category. Each entry's decided is a subset of the Esports record's, read in the same statement from the same daily rebuild.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub games: Option<Vec<TraderEsportsGameRecord>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderCategoryRecords {
/// Prefixed trader id (trd_<wallet>).
pub id: String,
/// Resolved wallet address, lowercased.
pub address: String,
/// Which sample the counts come from: every settled market in the category, at any position size. category_strengths on the trader endpoint reads the floored calibration sample instead, so the two differ in both directions: measured on production 2026-09-22 over the 122,497 wallet-category pairs with at least 20 decided markets on both bases, the floored rate was higher in 56.5% of pairs and lower in 34.5%, median +0.6 points, p10 -4.6, p90 +9.8, with 14.0% of pairs 10 points apart or more. Neither corrects the other.
pub basis: String,
/// Decided markets a category needs before win_rate is served. Published so a caller can apply its own sample rule to the raw counts.
pub min_decided_for_win_rate: i64,
/// When the served records were last rebuilt (RFC3339, Z): the newest stamp among every row served, game rows included, which the daily rebuild writes in one transaction. Null when the wallet has no record at all.
#[serde(default)]
pub computed_at: Option<String>,
/// One entry per canonical category with at least one decided market, ordered by decided descending then category. A category with no decided market is omitted. The Esports entry carries its per-game records in games.
pub records: Vec<TraderCategoryRecord>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderContext {
pub trader: Trader,
/// Aggregate position and P&L coverage for the trader. Counts all Polymarket markets, not only sports and esports. Omitted entirely (key absent, never null) when the trader is not in the local database or native net economics is unavailable. Realized fields remain numbers when present, including a genuine zero.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub position_summary: Option<TraderContextPositionSummary>,
/// RFC3339 freshness of the OPEN-position-level data: when a position_summary is present this mirrors its as_of byte-for-byte (latest /positions snapshot, else last completed sync) -- the open-position freshness clock, since the snapshot advances only open positions; otherwise the trader's last completed sync, even if an unserved position snapshot exists. Null when the selected clock is unavailable; a missing breakdown does not establish position or accounting freshness.
#[serde(default)]
pub data_as_of: Option<String>,
/// Human-readable statement of the point-in-time snapshot semantics (data_as_of is the open-position clock -- the latest /positions snapshot, else the last completed sync; the snapshot advances only open positions, so resolved counts and win rate can include settled markets recorded after the last full sync; native realized P&L follows its accounting snapshot -- not a live feed; re-fetch for fresher data).
pub freshness_note: String,
}
/// Aggregate position and P&L coverage for the trader. Counts all Polymarket markets, not only sports and esports. Omitted entirely (key absent, never null) when the trader is not in the local database or native net economics is unavailable. Realized fields remain numbers when present, including a genuine zero.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderContextPositionSummary {
/// Provider markets_traded count when known; null when the provider total is unavailable.
#[serde(default)]
pub markets_total: Option<i64>,
/// Markets with locally synced position rows.
pub markets_synced: i64,
/// Synced markets that have resolved.
pub markets_resolved: i64,
/// Synced markets still open with residual value (open positions with current value > 0).
pub markets_open: i64,
/// markets_synced divided by markets_total when the provider total is known (capped at 1.0); 0.0-1.0. When markets_total is unknown or not positive (null, 0, or negative), this is 1.0 if any markets are synced (else 0.0), so cross-check markets_total before treating 1.0 as full coverage.
pub sync_coverage: f64,
/// Native net realized P&L (USD) including credited rebates, minus the stored realized component of open positions with positive current value. Rebates are credited to the account, not to a position, so every credited rebate stays in this figure. NUMERIC arithmetic precedes conversion to a JSON number.
pub synced_realized_pnl: f64,
/// Native net realized P&L (USD) including credited rebates, read directly from the accounting snapshot. Position sync_coverage is separate from this accounting basis.
pub total_realized_pnl: f64,
/// Mark-to-market unrealized P&L (USD) on open positions.
pub unrealized_mtm: f64,
/// Cost basis (USD) currently tied up in open positions.
pub cost_basis_locked: f64,
/// Win rate as a percentage (0-100): resolved_wins over resolved_decided. A resolved market that settled at exactly zero realized P&L (a void, a refund, a fully hedged position) is neither a win nor a loss and is not in the denominator, the same rule the leaderboard applies; null when no resolved market was decided. It is the rule of trader.stats.win_rate, read from the synced rows at request time; stats.win_rate is the stored figure the profile shows, refreshed by each sync and a few minutes after a closed-history write. context.md prints one Market win rate: this one when position_summary is present, else stats.win_rate.
#[serde(default)]
pub resolved_win_rate: Option<f64>,
/// Resolved markets that settled at positive realized P&L: the win-rate numerator. Additive; null only on a summary cached before the field existed.
#[serde(default)]
pub resolved_wins: Option<i64>,
/// Resolved markets that settled at a non-zero realized P&L: the win-rate denominator. At most markets_resolved; the difference is markets settled at exactly zero or with an unknown P&L. Additive; null only on a summary cached before the field existed.
#[serde(default)]
pub resolved_decided: Option<i64>,
/// RFC3339 freshness of the served OPEN-position data: the trader's latest /positions snapshot, else the last completed sync. The snapshot advances ONLY open positions, so read this as the open-position freshness clock -- the resolved/closed aggregates on this same summary (markets_resolved, resolved_win_rate) advance only on a full sync. The value is the /positions snapshot instant when one exists, so it is typically at or just before the trader-level last sync on a normal sync, and can be AFTER it while the active-view loop refreshes open positions between full syncs. This clock does not establish native accounting freshness. Always present on the wire (serialized as JSON null when the trader has neither a snapshot nor a sync); never omitted.
#[serde(default)]
pub as_of: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderEsportsGameRecord {
/// The game, by the same name every holder chip carries in category_win_rate_game: LoL, CS2, Dota 2, Valorant, Call of Duty, Honor of Kings, Mobile Legends: Bang Bang, Overwatch, Rainbow Six Siege, Rocket League or StarCraft II. Match a chip to this row by string equality.
pub game: String,
/// The Polymarket series slug the record is keyed on: league-of-legends, counter-strike, dota-2, valorant, call-of-duty, honor-of-kings, mobile-legends-bang-bang, overwatch, rainbow-six-siege, rocket-league or starcraft-2. The stable key; game is the label.
pub series_slug: String,
/// Markets in this game the wallet closed with a profit.
pub wins: i64,
/// Markets in this game it closed with a profit or a loss. A subset of the parent Esports record's decided: the two are read from the same daily rebuild.
pub decided: i64,
/// wins / decided in [0,1], truncated to four decimals. Null when status is not_enough_data.
#[serde(default)]
pub win_rate: Option<f64>,
/// The same rule as the parent record: measured when decided cleared min_decided_for_win_rate, not_enough_data when the counts are real but the sample is under the floor. A chip on a market in this game shows this row's rate when it is measured and the Esports bucket's otherwise.
pub status: TraderEsportsGameRecordStatus,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TraderEsportsGameRecordStatus {
/// `measured`
#[serde(rename = "measured")]
Measured,
/// `not_enough_data`
#[serde(rename = "not_enough_data")]
NotEnoughData,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl TraderEsportsGameRecordStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Measured => "measured",
Self::NotEnoughData => "not_enough_data",
Self::Other(value) => value,
}
}
}
impl From<&str> for TraderEsportsGameRecordStatus {
fn from(value: &str) -> Self {
match value {
"measured" => Self::Measured,
"not_enough_data" => Self::NotEnoughData,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for TraderEsportsGameRecordStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for TraderEsportsGameRecordStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportArtifactManifest {
/// Version of the artifact manifest contract.
pub manifest_version: String,
/// Serialization used for the decompressed content.
pub format: Format,
/// Stable schema identifier for the selected serialization.
pub schema_version: SchemaVersion,
/// Sections represented by the artifact. JSON and NDJSON carry the full envelope and trades; CSV carries trade rows only.
pub coverage: Coverage,
pub generation: TraderExportGeneration,
/// Number of trade rows written.
pub row_count: i64,
/// Exact byte count of the decompressed content stream clients receive.
pub content_size_bytes: i64,
/// Lowercase SHA-256 of the decompressed content bytes.
pub content_sha256: String,
/// Exact byte count of the gzip-compressed bytes stored by the object provider.
pub compressed_size_bytes: i64,
/// Lowercase SHA-256 of the stored gzip bytes; the multipart ETag is not used as this checksum.
pub compressed_sha256: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportCategoryWatermark {
pub source: String,
pub coverage: String,
#[serde(default)]
pub publication_fence: Option<String>,
#[serde(default)]
pub data_as_of: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportGeneration {
/// Opaque generation identity selected for the coherent read snapshot.
pub id: String,
pub selected_at: String,
pub consistency: String,
pub source_watermarks: TraderExportSourceWatermarks,
/// The markets the file's trades and markets sections hold: sports and esports markets only (export_metadata.scope_note in a JSON or NDJSON file names the sections that still cover every market). Absent on a file written before 2026-10-22, which holds every market; a submit never reuses such a file.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub product_scope: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportJob {
pub object: String,
pub data: TraderExportJobData,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportJobData {
pub job_id: i64,
/// queued: accepted, not started. running: the worker is streaming rows. reconcile_required: the upload finished but the storage completion answer was lost; the hourly reconciler reads the object back and moves the job to ready or failed, and expires_at bounds the wait. ready: downloadable until expires_at. failed: terminal; error says why; submit a new export. expired: the retention window passed; the file is retired, the download route answers 410, submit a new export. cancel_requested: the owner cancelled a running job (POST /api/v1/trader/{address}/export/cancel); the worker stops at its next safe point and the job reads cancelled. cancelled: terminal; the owner cancelled the job and no file was published; submit a new export. A job that has not reached ready by expires_at reads failed with error 'export expired before completion'. failed, cancelled and expired rows stay readable for 48 hours, then the job answers 404.
pub status: TraderExportJobDataStatus,
pub format: Format,
/// Trade rows the file holds: trades in the wallet's sports and esports markets, counted in the file's own read snapshot. Null until the worker has read the snapshot (queued, and early in running); a job accepted before 2026-10-22 can carry the earlier all-markets estimate until its file is read.
#[serde(default)]
pub total_trades: Option<i64>,
/// Trade rows written so far; equals total_trades once the file is read.
#[serde(default)]
pub processed_trades: Option<i64>,
#[serde(default)]
pub file_size: Option<i64>,
#[serde(default)]
pub error: Option<String>,
/// True when status never changes again (ready, failed, expired, cancelled). Stop polling.
pub terminal: bool,
/// What to do next: poll the status route after poll_after_s, follow the download route, or submit a new export. Published beside status so a status value added later does not strand a client.
pub next_action: NextAction,
/// Seconds to wait before polling again. Absent when terminal. 5 while queued, running or cancel_requested; 300 while reconcile_required, the cadence that state can change at.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub poll_after_s: Option<i64>,
pub created_at: String,
/// When the worker last claimed the job; null while queued.
#[serde(default)]
pub started_at: Option<String>,
/// When the file became downloadable. null before ready, and on jobs finalized before this field existed.
#[serde(default)]
pub ready_at: Option<String>,
#[serde(default)]
pub failed_at: Option<String>,
/// The retention window: 24 hours from submit. A ready file downloads until this instant; a job that has not reached ready by it fails. A reused job (200 on submit) keeps its original window.
pub expires_at: String,
/// When the job became expired; null until then.
#[serde(default)]
pub expired_at: Option<String>,
/// What the file is a snapshot of: the trader's served-data clock (the latest position refresh, else the last completed sync) when the file was written; the same value as export_metadata.data_as_of inside the file. null until the file is written, or when the trader had neither. Read this, not ready_at, to decide whether a reused job is fresh enough; submit with fresh=true for a newer snapshot.
#[serde(default)]
pub data_as_of: Option<String>,
/// When the owner asked to cancel the job; null otherwise. Set on every cancelled job, including one cancelled while queued. While status is cancel_requested this is the instant the worker was asked to stop.
#[serde(default)]
pub cancel_requested_at: Option<String>,
/// When the job reached cancelled; null until then.
#[serde(default)]
pub cancelled_at: Option<String>,
/// Worker claims so far.
pub attempt: i64,
/// The job fails when attempt reaches this.
pub max_attempts: i64,
/// Present only while status is ready: the stored object's identity, so a client can check the download it receives.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub artifact: Option<TraderExportJobDataArtifact>,
}
/// Present only while status is ready: the stored object's identity, so a client can check the download it receives.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportJobDataArtifact {
/// Stable identity for this completed export artifact; unchanged when a temporary download URL is renewed.
pub artifact_id: String,
/// The storage ETag of the object.
#[serde(default)]
pub etag: Option<String>,
/// Bytes on the wire (gzip); file_size is the decompressed size.
#[serde(default)]
pub compressed_size_bytes: Option<i64>,
pub content_type: ContentType,
pub content_encoding: String,
/// Immutable manifest for artifacts generated with manifest support; null on historical artifacts written before this contract.
#[serde(default)]
pub manifest: Option<TraderExportArtifactManifest>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TraderExportJobDataStatus {
/// `queued`
#[serde(rename = "queued")]
Queued,
/// `running`
#[serde(rename = "running")]
Running,
/// `ready`
#[serde(rename = "ready")]
Ready,
/// `failed`
#[serde(rename = "failed")]
Failed,
/// `reconcile_required`
#[serde(rename = "reconcile_required")]
ReconcileRequired,
/// `expired`
#[serde(rename = "expired")]
Expired,
/// `cancel_requested`
#[serde(rename = "cancel_requested")]
CancelRequested,
/// `cancelled`
#[serde(rename = "cancelled")]
Cancelled,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl TraderExportJobDataStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Queued => "queued",
Self::Running => "running",
Self::Ready => "ready",
Self::Failed => "failed",
Self::ReconcileRequired => "reconcile_required",
Self::Expired => "expired",
Self::CancelRequested => "cancel_requested",
Self::Cancelled => "cancelled",
Self::Other(value) => value,
}
}
}
impl From<&str> for TraderExportJobDataStatus {
fn from(value: &str) -> Self {
match value {
"queued" => Self::Queued,
"running" => Self::Running,
"ready" => Self::Ready,
"failed" => Self::Failed,
"reconcile_required" => Self::ReconcileRequired,
"expired" => Self::Expired,
"cancel_requested" => Self::CancelRequested,
"cancelled" => Self::Cancelled,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for TraderExportJobDataStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for TraderExportJobDataStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportPnlWatermark {
pub source: String,
pub coverage: String,
#[serde(default)]
pub revision: Option<i64>,
#[serde(default)]
pub observed_at: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportPositionWatermark {
pub source: String,
pub coverage: String,
#[serde(default)]
pub generation: Option<i64>,
#[serde(default)]
pub data_as_of: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportSnapshot {
pub address: String,
/// The markets the export file's trades and markets sections hold: sports and esports markets only. counts.exported_markets, counts.estimated_trade_rows, counts.estimated_size_mb, reconciliation.exported_market_cost_basis and source_range.latest_trade_at and latest_market_activity_at describe those markets. The pnl dates, pnl_days, exported_activity_volume and completeness cover every market of the wallet.
pub product_scope: String,
pub generated_at: String,
pub source_range: ExportSourceRange,
pub completeness: ExportCompleteness,
pub reconciliation: ExportVolumeReconciliation,
pub counts: ExportCounts,
pub large_export_policy: LargeExportPolicy,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportSourceWatermarks {
pub positions: TraderExportPositionWatermark,
pub pnl: TraderExportPnlWatermark,
pub categories: TraderExportCategoryWatermark,
pub trades: TraderExportTradeWatermark,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderExportTradeWatermark {
pub source: String,
/// sports_esports_rows: the file holds every stored trade in the wallet's sports and esports markets. all_matching_rows on a file written before 2026-10-22, which holds every market's trades.
pub coverage: String,
/// Trade rows the file holds, counted in the same read snapshot as the rows.
pub rows: i64,
#[serde(default)]
pub first_activity_date: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderGradeAt {
/// Prefixed trader id (trd_<wallet>).
pub id: String,
/// Resolved wallet address, lowercased.
pub address: String,
/// Requested event or decision time.
pub as_of: String,
/// graded has a proven grade; ungraded is a proven null grade; unknown has no valid historical observation.
pub status: GradeAtTradeStatus,
/// Grade only when status is graded; null otherwise.
#[serde(default)]
pub grade: Option<Grade>,
/// First proven visibility instant for this trader. Null when no observation exists. Earlier times remain unknown.
#[serde(default)]
pub available_from: Option<String>,
/// Proof row when status is graded or ungraded. Null when history is unknown.
#[serde(default)]
pub observation: Option<TraderGradeAtObservation>,
}
/// Proof row when status is graded or ungraded. Null when history is unknown.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderGradeAtObservation {
/// Immutable grade_forward_history observation id.
pub id: i64,
/// Previous immutable observation id when retained. A later grade change supersedes that observation but never erases it; this link does not assert why the grade changed.
#[serde(default)]
pub previous_observation_id: Option<i64>,
/// Lower bound: the grade writer had started the transition at this instant.
pub observed_at: String,
/// Upper bound: a later snapshot confirmed the observation was committed. This is not an exact commit timestamp.
pub published_by: String,
/// Writer-proven model family, such as grading. Null when unknown.
#[serde(default)]
pub model_version: Option<String>,
/// Exact writer build SHA when recorded. Null on old and non-model observations.
#[serde(default)]
pub model_build_sha: Option<String>,
/// Database-clock upper bound on when the grading pass read its cohort inputs. Not the event time or source freshness of every input; null when unknown.
#[serde(default)]
pub source_observed_by: Option<String>,
}
/// A trader's daily P&L object. `id` is always present. The five sections -- `entries`, `stats`, `monthly`, `year_totals`, `drawdown` -- are present unless the request's `sections` parameter excluded them, so a request that sends no `sections` always carries all five. An excluded section is absent from the object, never null and never an empty array.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnl {
/// Prefixed trader ID (`trd_...`).
pub id: String,
/// Representation parameters applied to this body. Present only when the request sent `from`, `to` or `sections`; omitted otherwise, which is what keeps a no-parameter response identical to the one this route served before the parameters existed.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub view: Option<TraderPnlView>,
/// RFC3339 served-freshness clock for this trader's PnL history = traders.daily_pnl_recomputed_at, when the daily_pnl read model this response is served from was last rebuilt; null when it has never been recomputed for the trader. Always present, so read it as a value that can be null rather than a key that can be missing.
#[serde(default)]
pub freshness_at: Option<String>,
/// Daily cumulative-P&L series (oldest-first). Present unless the request's `sections` excluded it, and clipped to `from`/`to` when either is set. cumulative_profit and total_pnl stay cumulative from the start of the stored history, so a clipped slice keeps the same economic meaning; view.anchor carries the point before the window.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entries: Option<Vec<TraderPnlEntriesItem>>,
/// Pre-derived period stats. Present unless the request's `sections` excluded it. Always the published all/90d/30d/7d windows over the full stored history; `from`/`to` never recompute them over the request's range.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stats: Option<TraderPnlStats>,
/// Per-month P&L aggregation. Present unless the request's `sections` excluded it. Whole months over the full stored history, never clipped to `from`/`to`, so a month is never a partial month.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub monthly: Option<Vec<TraderPnlMonthlyItem>>,
/// Per-year P&L totals (ascending by year). Present unless the request's `sections` excluded it. Whole years over the full stored history, never clipped to `from`/`to`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub year_totals: Option<Vec<TraderPnlYearTotalsItem>>,
/// Underwater (drawdown) series. Present unless the request's `sections` excluded it, and clipped to `from`/`to` when either is set. The running peak behind each point is the full-history peak, so a clipped slice is not rebased; view.anchor.drawdown carries the value at the point before the window.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub drawdown: Option<Vec<TraderPnlDrawdownItem>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlDrawdownItem {
pub date: String,
pub cumulative_profit: f64,
pub drawdown: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlEntriesItem {
pub date: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub markets_traded: Option<i64>,
/// Cash in USD this wallet traded on this UTC day, buys and sells, summed from the fills 0xinsider has stored for it. Not Polymarket's share count, and not the full-history stats.total_volume on GET /api/v1/trader/{address}. Omitted, with markets_traded, on a day above 20 times the wallet's median day.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_volume: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cumulative_profit: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_pnl: Option<f64>,
pub daily_change: f64,
}
/// Lossless counterparts for trader P&L values. The object is omitted when no trusted native realized-P&L snapshot is available.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlExact {
/// Native Polymarket realized P&L plus credited maker and taker rebates, with fees included, from the trusted matching `trader_trading_pnl.net_realized_pnl` snapshot.
pub realized: ExactDecimal,
}
/// A wallet's sports and esports record (#27311): Polymarket's published realized P&L (pnl.realized plus any published pnl.correction) split into the part earned on sports and esports markets. Fees, refunds and rebates stay in, apportioned by cost. A wallet wholly in sports keeps the published figure exactly; one with no sports market gets 0. Refreshed after each wallet sync; as_of dates it.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlInScope {
/// Names the split rule, currently sports_esports_split_v1. A new value means the rule changed; compare figures only within one basis.
pub basis: String,
/// Sports and esports realized P&L in USD, display-safe. null when the wallet has no published realized figure to split.
#[serde(default)]
pub realized_pnl: Option<f64>,
/// Sports and esports markets the wallet has traded, any status.
pub markets: i64,
/// Of those, the markets that have resolved.
pub markets_resolved: i64,
/// Share of decided sports and esports markets the wallet won, 0 to 1. null with no decided market.
#[serde(default)]
pub win_rate: Option<f64>,
/// The wallet's markets outside sports and esports, which 0xinsider no longer lists.
pub markets_out_of_scope: i64,
/// When the record behind these figures was computed.
pub as_of: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlMonthlyItem {
pub year: i64,
pub month: i64,
pub pnl: f64,
pub markets_traded: i64,
}
/// Pre-derived period stats. Present unless the request's `sections` excluded it. Always the published all/90d/30d/7d windows over the full stored history; `from`/`to` never recompute them over the request's range.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlStats {
pub all: TraderPnlStatsAll,
pub d90: TraderPnlStatsD90,
pub d30: TraderPnlStatsD30,
pub d7: TraderPnlStatsD7,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlStatsAll {
pub current: f64,
pub change: f64,
pub change_pct: f64,
pub best_day: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub best_day_date: Option<String>,
pub worst_day: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub worst_day_date: Option<String>,
pub win_days: i64,
pub loss_days: i64,
pub avg_day: f64,
pub rebase_anchor: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlStatsD30 {
pub current: f64,
pub change: f64,
pub change_pct: f64,
pub best_day: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub best_day_date: Option<String>,
pub worst_day: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub worst_day_date: Option<String>,
pub win_days: i64,
pub loss_days: i64,
pub avg_day: f64,
pub rebase_anchor: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlStatsD7 {
pub current: f64,
pub change: f64,
pub change_pct: f64,
pub best_day: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub best_day_date: Option<String>,
pub worst_day: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub worst_day_date: Option<String>,
pub win_days: i64,
pub loss_days: i64,
pub avg_day: f64,
pub rebase_anchor: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlStatsD90 {
pub current: f64,
pub change: f64,
pub change_pct: f64,
pub best_day: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub best_day_date: Option<String>,
pub worst_day: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub worst_day_date: Option<String>,
pub win_days: i64,
pub loss_days: i64,
pub avg_day: f64,
pub rebase_anchor: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlSummary {
/// Polymarket's all-time leaderboard P&L in USD, from Polymarket's latest observation of this wallet on its all-time leaderboard (dated by the leaderboard_pnl data_quality group). Omitted when that leaderboard has no current observation ranking the wallet; 0xinsider requests one, and never serves an older stored figure in its place. Kept for provider parity and back-compat. Not the profile's Total P&L and not realized plus unrealized: it can differ from net_total by a wide margin. Read net_total for the figure the 0xinsider profile shows.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total: Option<f64>,
/// Polymarket's net total P&L in USD: native Polymarket position P&L, with fees already included, plus credited maker and taker rebates (trader_trading_pnl.net_total_pnl). Truncated toward zero to cents before conversion to a JSON number. Omitted when the wallet has no native accounting snapshot; pnl.total is never substituted. The 0xinsider profile's Total P&L is this figure unless pnl.correction is present, then pnl.correction.net_total.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub net_total: Option<f64>,
/// The 0xinsider correction to Polymarket's P&L for neg-risk conversions. Present only when one is published; pnl.net_total and pnl.realized keep Polymarket's own figures, and correction.net_total and correction.realized add the amount to them.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub correction: Option<PnlCorrection>,
/// true while 0xinsider is still checking some of the wallet's converted neg-risk markets (not yet measured, measured and not yet checked for transfers, or its latest reads keep failing), so the correction may change, or arrive where none is published yet. Omitted otherwise.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub correction_pending: Option<bool>,
/// For pnl.realized, expand=trust marks a matching native accounting snapshot as computed: native Polymarket realized P&L plus credited maker and taker rebates, with fees already included. The exact numeric value is truncated toward zero to cents before conversion to a JSON number. Without a matching native accounting snapshot, pnl.realized is omitted and its trust metadata is unavailable. Raw total P&L is never substituted for realized P&L.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub realized: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unrealized: Option<f64>,
/// DEPRECATED, never sent. The local pnl_7d rollup over-counted P&L (#5416 class) and is no longer emitted. Read the provider-native weekly window from GET /api/trader/{address}/profile-summary instead.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_7d: Option<f64>,
/// DEPRECATED, never sent. The local pnl_30d rollup over-counted P&L (#5416 class) and is no longer emitted. Read the provider-native monthly window from GET /api/trader/{address}/profile-summary instead.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_30d: Option<f64>,
/// Additive lossless counterpart. Omitted when the trusted native realized-P&L source is unavailable; existing numeric fields keep their display-safe v1 semantics.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exact: Option<TraderPnlExact>,
/// Additive (#27311): the wallet's sports and esports record, the figure the 0xinsider profile headlines. Omitted until 0xinsider has computed the wallet's record; absent means not computed yet, never zero. The other pnl fields stay Polymarket's wallet-wide figures.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub in_scope: Option<TraderPnlInScope>,
}
/// Representation parameters applied to this body. Present only when the request sent `from`, `to` or `sections`; omitted otherwise, which is what keeps a no-parameter response identical to the one this route served before the parameters existed.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlView {
/// Inclusive UTC lower bound applied to the daily series; null when the request left the series unbounded below.
#[serde(default)]
pub from: Option<String>,
/// Inclusive UTC upper bound applied to the daily series; null when the request left the series unbounded above.
#[serde(default)]
pub to: Option<String>,
/// Sections this body carries, in the order the object publishes them, whatever order the query listed.
pub sections: Vec<Sections>,
/// Points in the full stored daily series, before the window. Compare with entries_in_window to tell a clipped chart from a wallet that really has that many days.
pub entries_total: i64,
/// Points of the daily series inside the window. Equal to entries_total when no bound narrowed it, and reported whether or not entries was among the requested sections.
pub entries_in_window: i64,
/// The last daily point strictly before `from`: the cumulative baseline the window was cut away from. null when the request set no lower bound, or when the window starts at or before the first stored point and the slice already begins at the start of the history. Cumulative values in `entries` and `drawdown` are never rebased to the window, so this is the figure to subtract for a window-relative change.
#[serde(default)]
pub anchor: Option<TraderPnlViewAnchor>,
}
/// The last daily point strictly before `from`: the cumulative baseline the window was cut away from. null when the request set no lower bound, or when the window starts at or before the first stored point and the slice already begins at the start of the history. Cumulative values in `entries` and `drawdown` are never rebased to the window, so this is the figure to subtract for a window-relative change.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlViewAnchor {
pub date: String,
/// Cumulative profit carried into the window; null when the stored point has none, the same nullability entries[].cumulative_profit has.
#[serde(default)]
pub cumulative_profit: Option<f64>,
/// Cumulative total P&L carried into the window; null on the same terms.
#[serde(default)]
pub total_pnl: Option<f64>,
/// Underwater value at the anchor date; null when that date has no drawdown point, which happens exactly when its cumulative_profit is null. The running peak behind the window is cumulative_profit minus drawdown (drawdown is zero or negative).
#[serde(default)]
pub drawdown: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderPnlYearTotalsItem {
pub year: i64,
pub pnl: f64,
}
/// Curated advanced risk/performance metrics (expand=quant_metrics or expand[]=quant_metrics). Omitted unless expanded and backed by a computed row strictly under six hours old; a missing row, NULL computed_at, or age of exactly six hours or more is stale and omitted. Provider-input changes may intentionally lag inside the bounded six-hour window. When present, all listed fields are present (each is a number or null); null means insufficient trade history and must not be treated as 0. The fixed field shape is unchanged.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderQuantMetrics {
/// Trader smart score on a 0..100 display scale; higher is stronger. Null when unavailable.
#[serde(default)]
pub smart_score: Option<f64>,
/// Copyability score, 0-100. Same base as smart_score minus penalties for traits that make a strategy hard to replicate: -20 if fewer than 50 markets traded, -15 if positions are highly concentrated, -15 if position sizing exceeds about 2x Kelly, -10 if the largest loss on one settled market exceeds 30% of the capital the wallet deployed across all its positions, -10 if edge is inconsistent; result clamped to 0-100. Higher means easier to follow. null when insufficient history.
#[serde(default)]
pub copy_score: Option<f64>,
/// Sharpe ratio over the trailing 30 days (risk-adjusted return; higher is better). Magnitude can be large for small samples. null when insufficient history.
#[serde(default)]
pub sharpe_30d: Option<f64>,
/// Sharpe ratio over the trailing 7 days (risk-adjusted return; higher is better). null when insufficient history.
#[serde(default)]
pub sharpe_7d: Option<f64>,
/// Gross profit divided by gross loss; greater than 1 is profitable. Capped at 1000 when there are effectively no losses. null when insufficient history.
#[serde(default)]
pub profit_factor: Option<f64>,
/// Share of the trader's last 30 days with realized P&L that closed positive, 0-1 (higher is more consistent). A day with no realized P&L is not one of them, so the window can span months. null below 10 such days; null never means 0.
#[serde(default)]
pub edge_consistency: Option<f64>,
/// Cross-sectional percentile rank of the trader's Sharpe ratio versus all traders, 0-100. null when insufficient history.
#[serde(default)]
pub sharpe_percentile: Option<f64>,
/// Cross-sectional percentile rank of profit factor versus all traders, 0-100. null when insufficient history.
#[serde(default)]
pub pf_percentile: Option<f64>,
/// Cross-sectional percentile rank of edge consistency versus all traders, 0-100. null when insufficient history.
#[serde(default)]
pub consistency_percentile: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderStats {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub markets_traded: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub win_rate: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub daily_win_rate: Option<f64>,
/// Full-history both-sides USD cash volume from Polymarket user-volume. Omitted without a verified observation; never leaderboard shares. Its observation time is published as the volume group in data_quality and as trust.total_volume.freshness.as_of, both trader_usd_volume.observed_at; synced_at is a different clock and is never a substitute for it.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_volume: Option<f64>,
/// Additive lossless counterpart to `total_volume`. Omitted when the verified provider observation is unavailable.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exact: Option<TraderStatsExact>,
}
/// Lossless counterparts for trader statistics. The object is omitted when the verified provider observation is unavailable.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderStatsExact {
/// stats.total_volume as an exact decimal: full-history both-sides cash volume in USD, Polymarket user-volume `volume_usdc`, from the verified `trader_usd_volume` observation. Never Polymarket's share count.
pub total_volume: ExactDecimal,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderStrategy {
/// Observed trading style identifier. New rows use two_sided, category_focused, high_activity, diversified, mixed, or unclassified, counted over the wallet's sports and esports markets only. Historical identifiers remain readable during normal reclassification; style does not predict skill or intent.
pub strategy_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidence: Option<f64>,
}
/// `trader_synced` data: a tracked wallet finished a sync pass and has a fresh grade. The deprecated spelling `whale_trader_synced` delivers the same object.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderSyncedData {
/// 0xinsider's numeric id for the wallet.
pub trader_id: i64,
/// Proxy wallet address.
pub wallet: String,
}
/// Field-level trust metadata returned only when GET /api/v1/trader/{address} includes expand=trust.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TraderTrust {
pub total_pnl: TrustMetadata,
pub realized_pnl: TrustMetadata,
pub net_total_pnl: TrustMetadata,
/// Present only with pnl.correction: a computed value owned by trader_pnl_corrections, dated by the newest measurement behind it.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pnl_correction: Option<TrustMetadata>,
pub unrealized_pnl: TrustMetadata,
pub markets_traded: TrustMetadata,
pub win_rate: TrustMetadata,
pub daily_win_rate: TrustMetadata,
pub total_volume: TrustMetadata,
pub grade: TrustMetadata,
pub score: TrustMetadata,
pub forecast_score: TrustMetadata,
pub forecast_evidence: TrustMetadata,
pub rank: TrustMetadata,
pub streak_tier: TrustMetadata,
pub strategy: TrustMetadata,
pub category_strengths: TrustMetadata,
pub quant_metrics: TrustMetadata,
pub last_active: TrustMetadata,
pub synced_at: TrustMetadata,
pub sync_status: TrustMetadata,
pub category_records: TrustMetadata,
pub category_skill_model: TrustMetadata,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TrendingWallet {
/// Prefixed trader ID (`trd_...`).
pub id: String,
pub address: String,
/// 1-based rank within the full ranked set (stable across pages).
pub rank: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
/// Official Polymarket avatar URL (profileImage).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub profile_image_url: Option<String>,
/// Real provider platform; surfaced, never coerced. Polymarket only.
pub platform: String,
/// Polymarket weekly/monthly P&L for the wallet in USD, taken from Polymarket's canonical leaderboard (data-api.polymarket.com/v2/leaderboard?time_period=week|month&sort_by=PNL). This is the ranking axis and the rows are returned in Polymarket's by-PNL order; it is the provider's number, not a locally summed realized-leaf total.
pub trending_pnl_usd: f64,
/// Both-sides cash volume over the window in USD, from Polymarket GET /v2/user-volume (volume_usdc). Omitted when Polymarket served no volume for the wallet: an absent observation, never zero. Polymarket tracks volume in whole UTC days, so this window is the whole-day span covering the requested one, which is not the exact span trending_pnl_usd was scored over.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window_volume_usd: Option<f64>,
/// Both-sides traded volume over the window in SHARES, from the Polymarket leaderboard row, whose own schema states the figure is never USD. Omitted when the row carried none.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub window_volume_shares: Option<f64>,
pub window_markets_traded: i64,
/// Distinct in-window UTC trade days from our trades (0 if the winner is not in our DB).
pub window_trade_days: i64,
/// All-time trader grade; a separate axis from streak_tier. Led by realized profit (the money actually banked, about 95 percent of the grade), with forecasting calibration, risk-adjusted returns, and consistency as the tie-breaker and proven-trader guardrails: any grade above C requires verified net-positive realized profit, and the top grades also require a real resolved-market track record plus a survivable drawdown. Relative, so it drifts as the cohort moves. Omitted when the trader is Unranked (fewer than 5 markets, or too little verified record to cohort-rank).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grade: Option<Grade>,
/// Hot-streak tier (trailing-7d cross-sectional percentile). Omitted when there is no recent activity.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub streak_tier: Option<StreakTier>,
/// Polymarket's net total P&L for the wallet in USD: native Polymarket position P&L across all markets, fees included, plus credited maker and taker rebates (trader_trading_pnl.net_total_pnl). The 0xinsider profile's Total P&L is this figure unless pnl_correction is present, then pnl_correction.net_total. Context only: it does not affect the ranking, which follows trending_pnl_usd. Omitted when the wallet is not in the 0xinsider database or has no native accounting snapshot; the Polymarket leaderboard all-time figure is never substituted.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub all_time_pnl_usd: Option<f64>,
/// The 0xinsider correction to Polymarket's P&L for neg-risk conversions. Present only when one is published; all_time_pnl_usd keeps Polymarket's own figure and pnl_correction.net_total is the profile's Total P&L.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pnl_correction: Option<PnlCorrection>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub all_time_score: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_synced: Option<String>,
/// Shape-only daily P&L sparkline across the window, derived from Polymarket's documented user-pnl cumulative curve (GET /v2/user-pnl) converted to per-day deltas. It conveys the trend of the curve only and is NOT guaranteed to sum to trending_pnl_usd, which is the canonical leaderboard total.
pub daily_pnl_series: Vec<TrendingWalletDailyPnlSeriesItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TrendingWalletDailyPnlSeriesItem {
pub date: String,
pub pnl_usd: f64,
}
/// Whether the described value or result set is complete for its stated contract.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TrustCompleteness {
pub status: TrustCompletenessStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TrustCompletenessStatus {
/// `complete`
#[serde(rename = "complete")]
Complete,
/// `partial`
#[serde(rename = "partial")]
Partial,
/// `not_computed`
#[serde(rename = "not_computed")]
NotComputed,
/// `not_applicable`
#[serde(rename = "not_applicable")]
NotApplicable,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl TrustCompletenessStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Complete => "complete",
Self::Partial => "partial",
Self::NotComputed => "not_computed",
Self::NotApplicable => "not_applicable",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for TrustCompletenessStatus {
fn from(value: &str) -> Self {
match value {
"complete" => Self::Complete,
"partial" => Self::Partial,
"not_computed" => Self::NotComputed,
"not_applicable" => Self::NotApplicable,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for TrustCompletenessStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for TrustCompletenessStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Freshness metadata for a trust-critical value. This is separate from transport cache fields in ResponseMeta.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TrustFreshness {
pub status: TrustFreshnessStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub as_of: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_age_s: Option<i64>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TrustFreshnessStatus {
/// `fresh`
#[serde(rename = "fresh")]
Fresh,
/// `refreshing`
#[serde(rename = "refreshing")]
Refreshing,
/// `stale`
#[serde(rename = "stale")]
Stale,
/// `not_live`
#[serde(rename = "not_live")]
NotLive,
/// `unknown`
#[serde(rename = "unknown")]
Unknown,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl TrustFreshnessStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Fresh => "fresh",
Self::Refreshing => "refreshing",
Self::Stale => "stale",
Self::NotLive => "not_live",
Self::Unknown => "unknown",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for TrustFreshnessStatus {
fn from(value: &str) -> Self {
match value {
"fresh" => Self::Fresh,
"refreshing" => Self::Refreshing,
"stale" => Self::Stale,
"not_live" => Self::NotLive,
"unknown" => Self::Unknown,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for TrustFreshnessStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for TrustFreshnessStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Shared source/freshness/reconciliation/completeness metadata for public API values that may be cached, stale, partial, computed, or provider-unavailable. Unavailable provider values must be represented with explicit metadata instead of fabricated zeros or empty arrays.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TrustMetadata {
pub source: TrustSource,
pub freshness: TrustFreshness,
pub reconciliation: TrustReconciliation,
pub completeness: TrustCompleteness,
}
/// How provider-owned facts were reconciled with stored/read-model values.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TrustReconciliation {
pub status: TrustReconciliationStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TrustReconciliationStatus {
/// `provider_backed`
#[serde(rename = "provider_backed")]
ProviderBacked,
/// `db_mirror`
#[serde(rename = "db_mirror")]
DbMirror,
/// `computed`
#[serde(rename = "computed")]
Computed,
/// `partial`
#[serde(rename = "partial")]
Partial,
/// `not_applicable`
#[serde(rename = "not_applicable")]
NotApplicable,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl TrustReconciliationStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::ProviderBacked => "provider_backed",
Self::DbMirror => "db_mirror",
Self::Computed => "computed",
Self::Partial => "partial",
Self::NotApplicable => "not_applicable",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for TrustReconciliationStatus {
fn from(value: &str) -> Self {
match value {
"provider_backed" => Self::ProviderBacked,
"db_mirror" => Self::DbMirror,
"computed" => Self::Computed,
"partial" => Self::Partial,
"not_applicable" => Self::NotApplicable,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for TrustReconciliationStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for TrustReconciliationStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Source metadata for a trust-critical value. Providers and DB/read models own business truth; clients should not infer missing provider facts from titles, slugs, zeros, or empty arrays.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TrustSource {
pub kind: TrustSourceKind,
/// Provider, table/read-model, cache, or service that owns the value.
pub owner: String,
/// Provider field, DB column, or computed field name when applicable.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub field: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TrustSourceKind {
/// `provider`
#[serde(rename = "provider")]
Provider,
/// `database`
#[serde(rename = "database")]
Database,
/// `cache`
#[serde(rename = "cache")]
Cache,
/// `computed`
#[serde(rename = "computed")]
Computed,
/// `client_input`
#[serde(rename = "client_input")]
ClientInput,
/// `unavailable`
#[serde(rename = "unavailable")]
Unavailable,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl TrustSourceKind {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Provider => "provider",
Self::Database => "database",
Self::Cache => "cache",
Self::Computed => "computed",
Self::ClientInput => "client_input",
Self::Unavailable => "unavailable",
Self::Other(value) => value,
}
}
}
impl From<&str> for TrustSourceKind {
fn from(value: &str) -> Self {
match value {
"provider" => Self::Provider,
"database" => Self::Database,
"cache" => Self::Cache,
"computed" => Self::Computed,
"client_input" => Self::ClientInput,
"unavailable" => Self::Unavailable,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for TrustSourceKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for TrustSourceKind {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct UpdateWebhookRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
/// Replacement public HTTPS callback URL on the default port 443, validated and checked for uniqueness exactly like the create url. HTTPS scheme/host case, trailing DNS dots and port 443 normalize; path/query case is preserved. Changing it resets status to pending_verification and returns a new verification token; the new destination must pass the verification challenge before deliveries resume.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event_types: Option<Vec<WebhookEventType>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trade_filters: Option<LargeTradeSubscriptionFilters>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UpdateWebhookResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Usage {
pub object: String,
pub data: UsageData,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UsageData {
pub rate_limit: UsageDataRateLimit,
/// UTC-day usage count. Current V1 has no daily hard cap, so limit and remaining are null rather than synthesized.
pub daily_usage: UsageDataDailyUsage,
/// The monthly request quota (#16111): where the account stands against the requests the current Pro or Max plan includes per UTC calendar month. Reading it here spends nothing. null only when the month's count could not be read for this response.
#[serde(default)]
pub monthly_quota: Option<UsageDataMonthlyQuota>,
}
/// UTC-day usage count. Current V1 has no daily hard cap, so limit and remaining are null rather than synthesized.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UsageDataDailyUsage {
pub used: i64,
#[serde(default)]
pub limit: Option<i64>,
#[serde(default)]
pub remaining: Option<i64>,
pub reset_at: i64,
pub window_seconds: i64,
}
/// The monthly request quota (#16111): where the account stands against the requests the current Pro or Max plan includes per UTC calendar month. Reading it here spends nothing. null only when the month's count could not be read for this response.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UsageDataMonthlyQuota {
/// Admitted requests so far this UTC calendar month.
pub used: i64,
/// Requests the account's plan includes per UTC calendar month: 500,000 for Pro, 2,000,000 for Max.
pub limit: i64,
pub remaining: i64,
/// Unix seconds: the first instant of the next UTC calendar month.
pub reset_at: i64,
/// Unix seconds: from when request limit + 1 is refused for an account the quota binds.
pub enforced_from: i64,
/// Whether requests over this account's ceiling are refused right now: false before enforced_from and for an admin account.
pub binding: bool,
/// Pay as you go is on: requests over the quota keep answering and bill on a monthly invoice, up to ceiling.
pub pay_as_you_go: bool,
/// The most requests this account is admitted in a UTC calendar month once enforcement has begun: limit, or four times limit with pay as you go on (2,000,000 for Pro). null for an admin account, which no number stops. Additive since 2026-09-22.
#[serde(default)]
pub ceiling: Option<i64>,
/// Why pay as you go is unavailable, or null. usage_price_reconciliation_required: the account's usage subscription carries a historical price whose included requests differ from its plan's, so pay as you go stays off (pay_as_you_go false) until billing reconciles it. Always sent since 2026-10-17.
#[serde(default)]
pub unavailable_reason: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UsageDataRateLimit {
pub used: i64,
/// The per-minute window's limit for the caller's plan: 100 requests on Pro, 200 on Max. The same number RateLimit-Limit reports on a counted request.
pub limit: i64,
pub remaining: i64,
pub reset_at: i64,
pub window_seconds: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VerifyWebhookRequest {
/// The one-time token returned on create or on a url change. It is necessary but not sufficient: the destination must also answer the signed webhook.verification challenge with a 2xx.
pub verification_token: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct VerifyWebhookResponse {
pub object: String,
pub data: WebhookEndpoint,
pub meta: ResponseMeta,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum View {
/// `board`
#[serde(rename = "board")]
Board,
/// `signals`
#[serde(rename = "signals")]
Signals,
/// `observations`
#[serde(rename = "observations")]
Observations,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl View {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Board => "board",
Self::Signals => "signals",
Self::Observations => "observations",
Self::Other(value) => value,
}
}
}
impl From<&str> for View {
fn from(value: &str) -> Self {
match value {
"board" => Self::Board,
"signals" => Self::Signals,
"observations" => Self::Observations,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for View {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for View {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// `wallet_grade_changed` data (Pro and Max): a tracked wallet's grade moved on a re-grade.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WalletGradeChangedData {
/// Lowercased proxy wallet address.
pub wallet: String,
/// 0xinsider's numeric id for the wallet.
pub trader_id: i64,
/// The grade before the move. Never equal to `new_grade`.
pub old_grade: Grade,
pub new_grade: Grade,
/// `upgrade` when `new_grade` is better than `old_grade`, else `downgrade`.
pub direction: WalletGradeChangedDataDirection,
/// Cohort skill index that drove the grade, 0 to 1.
pub skill_index: f64,
/// Final 0 to 100 score banded into the new grade.
pub final_score: f64,
/// The ranking date of the re-grade.
pub date: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum WalletGradeChangedDataDirection {
/// `upgrade`
#[serde(rename = "upgrade")]
Upgrade,
/// `downgrade`
#[serde(rename = "downgrade")]
Downgrade,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl WalletGradeChangedDataDirection {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Upgrade => "upgrade",
Self::Downgrade => "downgrade",
Self::Other(value) => value,
}
}
}
impl From<&str> for WalletGradeChangedDataDirection {
fn from(value: &str) -> Self {
match value {
"upgrade" => Self::Upgrade,
"downgrade" => Self::Downgrade,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for WalletGradeChangedDataDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for WalletGradeChangedDataDirection {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// Owner-scoped view of one webhook delivery attempt. Deliberately omits the request body and the endpoint signing secret: a delivery log never re-exposes the payload or any secret material.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WebhookDelivery {
pub id: i64,
pub object: String,
/// Stable event id for this delivery; identical across retries of the same logical event.
pub event_id: String,
pub event_type: WebhookEventType,
/// Delivery lifecycle state (e.g. pending, delivered, dead_letter).
pub status: String,
/// Number of delivery attempts made so far.
pub attempt_count: i64,
/// HTTP status code of the most recent delivery attempt. Omitted until a response (or transport error) has been recorded.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_response_status: Option<i64>,
/// Short description of the most recent delivery failure. Omitted when the last attempt succeeded or none has failed.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_error: Option<String>,
/// When the delivery was first accepted by the destination. Omitted until a delivery succeeds.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delivered_at: Option<String>,
/// When the next delivery attempt is due, for a delivery whose status is pending or retry. null while an attempt is in flight (processing) and once the delivery is terminal (delivered or dead_letter).
#[serde(default)]
pub next_attempt_at: Option<String>,
/// Why next_attempt_at was scheduled: receiver_retry_after for an accepted Retry-After on a 408, 429, or 5xx response; transient_failure for a network or ordinary transient retry; permanent_or_auth_failure for another non-2xx response; manual_redelivery or configuration_changed for those queue actions. null while an attempt is in flight or once the delivery is terminal.
#[serde(default)]
pub retry_schedule_reason: Option<RetryScheduleReason>,
pub created_at: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WebhookEndpoint {
pub id: i64,
pub object: String,
pub name: String,
pub url: String,
pub event_types: Vec<WebhookEventType>,
pub trade_filters: LargeTradeSubscriptionFilters,
pub status: WebhookStatus,
#[serde(default)]
pub verified_at: Option<String>,
pub verification_token_expires_at: String,
pub failure_count: i64,
pub created_at: String,
pub updated_at: String,
pub retry_policy: WebhookRetryPolicy,
pub secret_rotation: WebhookSecretRotation,
/// Returned only on create, immediate rotate-secret, staged rotate-secret/prepare, or staged rotate-secret/activate. Never returned by list, get, update, delete, verify, or retire.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signing_secret: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub verification: Option<WebhookVerification>,
}
/// Self-describing entry in the webhook event catalog: the event type a subscriber lists in event_types, when it fires, the data payload shape, and whether it currently fires (active) or is reserved (dormant, subscribable but not yet delivered). Pro-only event types (large_trades_inserted, whale_trades_inserted, wallet_grade_changed, suspicious_trade_flagged, insider_radar_flag_raised, sharp_money_flow_detected, smart_money_flow_detected) only deliver to API keys on an active Pro subscription. Export lifecycle event types (export_job_ready, export_job_failed, export_job_expired, export_job_cancelled) are owner-scoped to the API-key account that created the export and contain no download URL; use the authorized export status/download routes. Some entries are two spellings of one event: large_trades_inserted and whale_trades_inserted, trader_synced and whale_trader_synced, suspicious_trade_flagged and insider_radar_flag_raised, sharp_money_flow_detected and smart_money_flow_detected. Either spelling subscribes, and an endpoint receives deliveries under the spelling it registered.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WebhookEventDescriptor {
pub id: WebhookEventType,
/// One-line description of when the event fires.
pub description: String,
/// Short description of the data payload object's shape.
pub payload_shape: String,
/// active: the event has a firing producer callsite. dormant: advertised and subscribable, but does not yet enqueue any delivery.
pub status: WebhookEventDescriptorStatus,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum WebhookEventDescriptorStatus {
/// `active`
#[serde(rename = "active")]
Active,
/// `dormant`
#[serde(rename = "dormant")]
Dormant,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl WebhookEventDescriptorStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Active => "active",
Self::Dormant => "dormant",
Self::Other(value) => value,
}
}
}
impl From<&str> for WebhookEventDescriptorStatus {
fn from(value: &str) -> Self {
match value {
"active" => Self::Active,
"dormant" => Self::Dormant,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for WebhookEventDescriptorStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for WebhookEventDescriptorStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum WebhookEventType {
/// `large_trade_inserted_v2`
#[serde(rename = "large_trade_inserted_v2")]
LargeTradeInsertedV2,
/// `large_trades_inserted`
#[serde(rename = "large_trades_inserted")]
LargeTradesInserted,
/// `whale_trades_inserted`
#[serde(rename = "whale_trades_inserted")]
WhaleTradesInserted,
/// `live_sports_updated`
#[serde(rename = "live_sports_updated")]
LiveSportsUpdated,
/// `trader_synced`
#[serde(rename = "trader_synced")]
TraderSynced,
/// `whale_trader_synced`
#[serde(rename = "whale_trader_synced")]
WhaleTraderSynced,
/// `large_positions_updated`
#[serde(rename = "large_positions_updated")]
LargePositionsUpdated,
/// `wallet_grade_changed`
#[serde(rename = "wallet_grade_changed")]
WalletGradeChanged,
/// `suspicious_trade_flagged`
#[serde(rename = "suspicious_trade_flagged")]
SuspiciousTradeFlagged,
/// `insider_radar_flag_raised`
#[serde(rename = "insider_radar_flag_raised")]
InsiderRadarFlagRaised,
/// `sharp_money_flow_detected`
#[serde(rename = "sharp_money_flow_detected")]
SharpMoneyFlowDetected,
/// `smart_money_flow_detected`
#[serde(rename = "smart_money_flow_detected")]
SmartMoneyFlowDetected,
/// `export_job_ready`
#[serde(rename = "export_job_ready")]
ExportJobReady,
/// `export_job_failed`
#[serde(rename = "export_job_failed")]
ExportJobFailed,
/// `export_job_expired`
#[serde(rename = "export_job_expired")]
ExportJobExpired,
/// `export_job_cancelled`
#[serde(rename = "export_job_cancelled")]
ExportJobCancelled,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl WebhookEventType {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::LargeTradeInsertedV2 => "large_trade_inserted_v2",
Self::LargeTradesInserted => "large_trades_inserted",
Self::WhaleTradesInserted => "whale_trades_inserted",
Self::LiveSportsUpdated => "live_sports_updated",
Self::TraderSynced => "trader_synced",
Self::WhaleTraderSynced => "whale_trader_synced",
Self::LargePositionsUpdated => "large_positions_updated",
Self::WalletGradeChanged => "wallet_grade_changed",
Self::SuspiciousTradeFlagged => "suspicious_trade_flagged",
Self::InsiderRadarFlagRaised => "insider_radar_flag_raised",
Self::SharpMoneyFlowDetected => "sharp_money_flow_detected",
Self::SmartMoneyFlowDetected => "smart_money_flow_detected",
Self::ExportJobReady => "export_job_ready",
Self::ExportJobFailed => "export_job_failed",
Self::ExportJobExpired => "export_job_expired",
Self::ExportJobCancelled => "export_job_cancelled",
Self::Other(value) => value,
}
}
}
impl From<&str> for WebhookEventType {
fn from(value: &str) -> Self {
match value {
"large_trade_inserted_v2" => Self::LargeTradeInsertedV2,
"large_trades_inserted" => Self::LargeTradesInserted,
"whale_trades_inserted" => Self::WhaleTradesInserted,
"live_sports_updated" => Self::LiveSportsUpdated,
"trader_synced" => Self::TraderSynced,
"whale_trader_synced" => Self::WhaleTraderSynced,
"large_positions_updated" => Self::LargePositionsUpdated,
"wallet_grade_changed" => Self::WalletGradeChanged,
"suspicious_trade_flagged" => Self::SuspiciousTradeFlagged,
"insider_radar_flag_raised" => Self::InsiderRadarFlagRaised,
"sharp_money_flow_detected" => Self::SharpMoneyFlowDetected,
"smart_money_flow_detected" => Self::SmartMoneyFlowDetected,
"export_job_ready" => Self::ExportJobReady,
"export_job_failed" => Self::ExportJobFailed,
"export_job_expired" => Self::ExportJobExpired,
"export_job_cancelled" => Self::ExportJobCancelled,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for WebhookEventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for WebhookEventType {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WebhookRetryPolicy {
pub max_attempts: i64,
pub terminal_status: String,
/// Total wait from a delivery's first failed attempt to its last retry: 60, 120, 240, 480, 960, 1920 and 3600 seconds. A delivery still failing after that is dead_letter.
pub retry_horizon_seconds: i64,
/// Consecutive failed attempts, across all of this endpoint's deliveries, after which the endpoint is disabled and its queued deliveries are dead-lettered. Any successful attempt resets the count.
pub disable_after_consecutive_failures: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WebhookSecretRotation {
/// idle when no staged rotation exists, pending after prepare, and overlap after activate while both signing secrets are accepted.
pub status: WebhookSecretRotationStatus,
/// When the previous signing secret stops being emitted and accepted. null outside the overlap phase.
#[serde(default)]
pub overlap_expires_at: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum WebhookSecretRotationStatus {
/// `idle`
#[serde(rename = "idle")]
Idle,
/// `pending`
#[serde(rename = "pending")]
Pending,
/// `overlap`
#[serde(rename = "overlap")]
Overlap,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl WebhookSecretRotationStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Idle => "idle",
Self::Pending => "pending",
Self::Overlap => "overlap",
Self::Other(value) => value,
}
}
}
impl From<&str> for WebhookSecretRotationStatus {
fn from(value: &str) -> Self {
match value {
"idle" => Self::Idle,
"pending" => Self::Pending,
"overlap" => Self::Overlap,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for WebhookSecretRotationStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for WebhookSecretRotationStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum WebhookStatus {
/// `pending_verification`
#[serde(rename = "pending_verification")]
PendingVerification,
/// `active`
#[serde(rename = "active")]
Active,
/// `disabled`
#[serde(rename = "disabled")]
Disabled,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl WebhookStatus {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::PendingVerification => "pending_verification",
Self::Active => "active",
Self::Disabled => "disabled",
Self::Other(value) => value,
}
}
}
impl From<&str> for WebhookStatus {
fn from(value: &str) -> Self {
match value {
"pending_verification" => Self::PendingVerification,
"active" => Self::Active,
"disabled" => Self::Disabled,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for WebhookStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for WebhookStatus {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WebhookVerification {
/// One-time verification token returned only on create or URL change. Pass it to POST /api/v1/webhooks/{id}/verify, which activates the endpoint only when the destination also answers the signed webhook.verification challenge with a 2xx.
pub token: String,
pub expires_at: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WebhookVerificationAttempt {
pub id: String,
pub object: String,
pub webhook_id: i64,
/// queued/running are pending consent. verified means the same endpoint revision answered 2xx and activated atomically. failed/cancelled/expired are terminal and do not restart on polling or replay.
pub state: WebhookVerificationAttemptState,
/// Sanitized last observation. Null before an observation; queued retries may retain their previous failure outcome.
#[serde(default)]
pub outcome: Option<WebhookVerificationAttemptOutcome>,
pub attempt_count: i64,
pub max_attempts: i64,
/// Earlier of the verification-token deadline and 15 minutes after admission. Activation is forbidden at or after this instant.
pub expires_at: String,
/// Scheduled retry/admission time while queued; null while running or terminal.
#[serde(default)]
pub next_attempt_at: Option<String>,
/// Actual receiver HTTP status, when one arrived. No receiver response body or resolved address is returned.
#[serde(default)]
pub last_response_status: Option<i64>,
#[serde(default)]
pub started_at: Option<String>,
#[serde(default)]
pub completed_at: Option<String>,
pub created_at: String,
pub updated_at: String,
/// Relative authorized API path for this attempt. Uses the same bearer credential as admission.
pub status_url: String,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum WebhookVerificationAttemptOutcome {
/// `verified`
#[serde(rename = "verified")]
Verified,
/// `target_rejected`
#[serde(rename = "target_rejected")]
TargetRejected,
/// `challenge_timeout`
#[serde(rename = "challenge_timeout")]
ChallengeTimeout,
/// `transport_failed`
#[serde(rename = "transport_failed")]
TransportFailed,
/// `receiver_rejected`
#[serde(rename = "receiver_rejected")]
ReceiverRejected,
/// `token_expired`
#[serde(rename = "token_expired")]
TokenExpired,
/// `endpoint_changed`
#[serde(rename = "endpoint_changed")]
EndpointChanged,
/// `owner_revoked`
#[serde(rename = "owner_revoked")]
OwnerRevoked,
/// `attempts_exhausted`
#[serde(rename = "attempts_exhausted")]
AttemptsExhausted,
/// `internal_error`
#[serde(rename = "internal_error")]
InternalError,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl WebhookVerificationAttemptOutcome {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Verified => "verified",
Self::TargetRejected => "target_rejected",
Self::ChallengeTimeout => "challenge_timeout",
Self::TransportFailed => "transport_failed",
Self::ReceiverRejected => "receiver_rejected",
Self::TokenExpired => "token_expired",
Self::EndpointChanged => "endpoint_changed",
Self::OwnerRevoked => "owner_revoked",
Self::AttemptsExhausted => "attempts_exhausted",
Self::InternalError => "internal_error",
Self::Other(value) => value,
}
}
}
impl From<&str> for WebhookVerificationAttemptOutcome {
fn from(value: &str) -> Self {
match value {
"verified" => Self::Verified,
"target_rejected" => Self::TargetRejected,
"challenge_timeout" => Self::ChallengeTimeout,
"transport_failed" => Self::TransportFailed,
"receiver_rejected" => Self::ReceiverRejected,
"token_expired" => Self::TokenExpired,
"endpoint_changed" => Self::EndpointChanged,
"owner_revoked" => Self::OwnerRevoked,
"attempts_exhausted" => Self::AttemptsExhausted,
"internal_error" => Self::InternalError,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for WebhookVerificationAttemptOutcome {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for WebhookVerificationAttemptOutcome {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum WebhookVerificationAttemptState {
/// `queued`
#[serde(rename = "queued")]
Queued,
/// `running`
#[serde(rename = "running")]
Running,
/// `verified`
#[serde(rename = "verified")]
Verified,
/// `failed`
#[serde(rename = "failed")]
Failed,
/// `cancelled`
#[serde(rename = "cancelled")]
Cancelled,
/// `expired`
#[serde(rename = "expired")]
Expired,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl WebhookVerificationAttemptState {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::Queued => "queued",
Self::Running => "running",
Self::Verified => "verified",
Self::Failed => "failed",
Self::Cancelled => "cancelled",
Self::Expired => "expired",
Self::Other(value) => value,
}
}
}
impl From<&str> for WebhookVerificationAttemptState {
fn from(value: &str) -> Self {
match value {
"queued" => Self::Queued,
"running" => Self::Running,
"verified" => Self::Verified,
"failed" => Self::Failed,
"cancelled" => Self::Cancelled,
"expired" => Self::Expired,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for WebhookVerificationAttemptState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for WebhookVerificationAttemptState {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WhaleDatasetArtifactManifest {
/// Version of the artifact manifest contract.
pub manifest_version: String,
pub format: String,
pub schema_version: String,
pub coverage: String,
pub generation: WhaleDatasetGeneration,
/// Number of trade rows written.
pub row_count: i64,
/// Exact byte count of the decompressed content stream clients receive.
pub content_size_bytes: i64,
/// Lowercase SHA-256 of the decompressed content bytes.
pub content_sha256: String,
/// Exact byte count of the gzip-compressed bytes stored by the object provider.
pub compressed_size_bytes: i64,
/// Lowercase SHA-256 of the stored gzip bytes; the multipart ETag is not used as this checksum.
pub compressed_sha256: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WhaleDatasetContinuation {
pub path: String,
/// Opaque commit-safe replay cursor; replay the same condition_id and min_size.
pub cursor: String,
/// Writer floor included by the replay cursor. It can precede the snapshot horizon to include arrivals after the finite window end.
pub from_commit_xid: String,
#[serde(default)]
pub condition_id: Option<String>,
/// Normalized USD threshold; pass it to the replay query.
#[serde(default)]
pub min_size: Option<String>,
pub deduplication_identity: String,
pub time_window_applies_to_deltas: bool,
pub delivery: String,
}
/// Normalized immutable request; [from, to), maximum 31 days; min_size rounded to cents like durable replay.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WhaleDatasetFilters {
pub from: String,
pub to: String,
#[serde(default)]
pub condition_id: Option<String>,
#[serde(default)]
pub min_size_cents: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WhaleDatasetGeneration {
pub dataset_schema_version: String,
pub id: String,
pub selected_at: String,
pub consistency: String,
pub filters: WhaleDatasetFilters,
/// All snapshot rows have a writer xid strictly below this closed visibility horizon. Decimal string preserves integer precision.
pub commit_horizon_xid: String,
pub continuation: WhaleDatasetContinuation,
pub source: String,
/// Best-effort detected whale alerts only; not all provider fills. No trader or market enrichment.
pub coverage: String,
pub expires_at: String,
pub extraction_elapsed_ms: i64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WhaleDatasetJob {
pub object: String,
pub data: WhaleDatasetJobData,
pub meta: ResponseMeta,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WhaleDatasetJobData {
pub job_id: i64,
/// queued: accepted, not started. running: the worker is streaming rows. reconcile_required: the upload finished but the storage completion answer was lost; the hourly reconciler reads the object back and moves the job to ready or failed, and expires_at bounds the wait. ready: downloadable until expires_at. failed: terminal; error says why; submit a new export. expired: the retention window passed; the file is retired, the download route answers 410, submit a new export. cancel_requested: the owner cancelled a running job (POST /api/v1/trader/{address}/export/cancel); the worker stops at its next safe point and the job reads cancelled. cancelled: terminal; the owner cancelled the job and no file was published; submit a new export. A job that has not reached ready by expires_at reads failed with error 'export expired before completion'. failed, cancelled and expired rows stay readable for 48 hours, then the job answers 404.
pub status: TraderExportJobDataStatus,
pub format: String,
#[serde(default)]
pub total_trades: Option<i64>,
#[serde(default)]
pub processed_trades: Option<i64>,
#[serde(default)]
pub file_size: Option<i64>,
#[serde(default)]
pub error: Option<String>,
/// True when status never changes again (ready, failed, expired, cancelled). Stop polling.
pub terminal: bool,
/// What to do next: poll the status route after poll_after_s, follow the download route, or submit a new export. Published beside status so a status value added later does not strand a client.
pub next_action: NextAction,
/// Seconds to wait before polling again. Absent when terminal. 5 while queued, running or cancel_requested; 300 while reconcile_required, the cadence that state can change at.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub poll_after_s: Option<i64>,
pub created_at: String,
/// When the worker last claimed the job; null while queued.
#[serde(default)]
pub started_at: Option<String>,
/// When the file became downloadable. null before ready, and on jobs finalized before this field existed.
#[serde(default)]
pub ready_at: Option<String>,
#[serde(default)]
pub failed_at: Option<String>,
/// The retention window: 24 hours from submit. A ready file downloads until this instant; a job that has not reached ready by it fails. A reused job (200 on submit) keeps its original window.
pub expires_at: String,
/// When the job became expired; null until then.
#[serde(default)]
pub expired_at: Option<String>,
/// Snapshot selection clock, null until the artifact is written; not provider completeness.
#[serde(default)]
pub data_as_of: Option<String>,
/// When the owner asked to cancel the job; null otherwise. Set on every cancelled job, including one cancelled while queued. While status is cancel_requested this is the instant the worker was asked to stop.
#[serde(default)]
pub cancel_requested_at: Option<String>,
/// When the job reached cancelled; null until then.
#[serde(default)]
pub cancelled_at: Option<String>,
/// Worker claims so far.
pub attempt: i64,
/// The job fails when attempt reaches this.
pub max_attempts: i64,
/// Present only while status is ready: the stored object's identity, so a client can check the download it receives.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub artifact: Option<WhaleDatasetJobDataArtifact>,
pub filters: WhaleDatasetFilters,
}
/// Present only while status is ready: the stored object's identity, so a client can check the download it receives.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WhaleDatasetJobDataArtifact {
/// Stable identity for this completed export artifact; unchanged when a temporary download URL is renewed.
pub artifact_id: String,
/// The storage ETag of the object.
#[serde(default)]
pub etag: Option<String>,
/// Bytes on the wire (gzip); file_size is the decompressed size.
#[serde(default)]
pub compressed_size_bytes: Option<i64>,
pub content_type: String,
pub content_encoding: String,
/// Immutable source, window, replay handoff, count and hashes of both content and gzip bytes.
#[serde(default)]
pub manifest: Option<WhaleDatasetArtifactManifest>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct WhaleDatasetTrade {
/// wt_<whale_alert_id>; deduplication identity shared with expanded replay.
pub id: String,
/// Decimal whale-alert ID.
pub sequence: String,
/// trd_<local trader ID>.
pub trader_id: String,
pub condition_id: String,
pub platform: String,
pub traded_at: String,
pub side: LargeTradeInsertedV2DataSide,
/// Provider outcome index, null before the canonical outcome trust boundary.
#[serde(default)]
pub outcome_index: Option<i64>,
/// Exact stored NUMERIC price; decimal string.
pub price: String,
/// Exact stored NUMERIC USD notional; decimal string.
pub size_usd: String,
/// Writer transaction ID, decimal string.
pub inserted_xid: String,
/// Stored source trade identity.
pub trade_event_id: String,
#[serde(default)]
pub source_trade_ingested_at: Option<String>,
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Window {
/// `24h`
#[serde(rename = "24h")]
V24h,
/// `7d`
#[serde(rename = "7d")]
V7d,
/// `all`
#[serde(rename = "all")]
All,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl Window {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::V24h => "24h",
Self::V7d => "7d",
Self::All => "all",
Self::Other(value) => value,
}
}
}
impl From<&str> for Window {
fn from(value: &str) -> Self {
match value {
"24h" => Self::V24h,
"7d" => Self::V7d,
"all" => Self::All,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for Window {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for Window {
fn as_ref(&self) -> &str {
self.as_str()
}
}
/// A value this release does not know is kept in `Other`, so a new value never fails a response.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum WithdrawalReason {
/// `provider_game_cancelled`
#[serde(rename = "provider_game_cancelled")]
ProviderGameCancelled,
/// `published_after_kickoff`
#[serde(rename = "published_after_kickoff")]
PublishedAfterKickoff,
/// A value this release does not know, kept as sent.
#[serde(untagged)]
Other(String),
}
impl WithdrawalReason {
/// The value as the API spells it.
pub fn as_str(&self) -> &str {
match self {
Self::ProviderGameCancelled => "provider_game_cancelled",
Self::PublishedAfterKickoff => "published_after_kickoff",
Self::Other(value) => value,
}
}
}
impl From<&str> for WithdrawalReason {
fn from(value: &str) -> Self {
match value {
"provider_game_cancelled" => Self::ProviderGameCancelled,
"published_after_kickoff" => Self::PublishedAfterKickoff,
other => Self::Other(other.to_owned()),
}
}
}
impl std::fmt::Display for WithdrawalReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl AsRef<str> for WithdrawalReason {
fn as_ref(&self) -> &str {
self.as_str()
}
}