Skip to main content

Overview

The Market struct represents a prediction market. It contains all the information about a market including its question, outcomes, prices, trading metadata, and exchange-specific data.

Structure

pub struct Market {
    pub id: String,
    pub question: String,
    pub outcomes: Vec<String>,
    pub close_time: Option<DateTime<Utc>>,
    pub volume: f64,
    pub liquidity: f64,
    pub prices: HashMap<String, f64>,
    pub metadata: serde_json::Value,
    pub tick_size: f64,
    pub description: String,
    pub slug: Option<String>,
    pub event_id: Option<String>,
    pub condition_id: Option<String>,
    pub token_ids: Option<Vec<String>>,
    pub best_bid: Option<f64>,
    pub best_ask: Option<f64>,
    pub last_trade_price: Option<f64>,
    pub spread: Option<f64>,
    pub volume_24h: Option<f64>,
    pub daily_avg_volume: Option<f64>,
    pub open_interest: Option<f64>,
    pub category: Option<String>,
    pub status: Option<String>,
    pub created_at: Option<DateTime<Utc>>,
    pub exchange: Option<String>,
}

Fields

FieldTypeDescription
idStringUnique market identifier
questionStringThe prediction question
outcomesVec<String>Possible outcomes (e.g., ["Yes", "No"])
close_timeOption<DateTime<Utc>>When the market closes
volumef64Total lifetime trading volume in USD
liquidityf64Current market liquidity
pricesHashMap<String, f64>Current price for each outcome
metadataserde_json::ValueExchange-specific metadata
tick_sizef64Minimum price increment
descriptionStringDetailed market description
slugOption<String>Market slug (Polymarket, Limitless)
event_idOption<String>Event identifier (Polymarket conditionId, Kalshi event_ticker)
condition_idOption<String>On-chain condition or contract ID
token_idsOption<Vec<String>>CLOB token IDs for each outcome
best_bidOption<f64>Best bid price
best_askOption<f64>Best ask price
last_trade_priceOption<f64>Last trade price
spreadOption<f64>Pre-computed bid-ask spread
volume_24hOption<f64>24-hour trading volume
daily_avg_volumeOption<f64>Daily average volume
open_interestOption<f64>Open interest (outstanding contracts/positions)
categoryOption<String>Market category or tag
statusOption<String>Market status string
created_atOption<DateTime<Utc>>Creation timestamp
exchangeOption<String>Exchange identifier

Methods

is_binary()

Check if the market is a binary market (exactly 2 outcomes):
let market = exchange.fetch_market("market-id").await?;

if market.is_binary() {
    println!("This is a Yes/No market");
} else {
    println!("This market has {} outcomes", market.outcomes.len());
}

is_open()

Check if the market is currently open for trading:
if market.is_open() {
    println!("Market is open for trading");
} else {
    println!("Market is closed");
}
The method checks:
  1. The closed field in metadata (if present)
  2. Whether current time is before close_time

spread()

Calculate the market spread for binary markets (computed fallback):
if let Some(spread) = market.spread() {
    println!("Market spread: {:.2}%", spread * 100.0);
} else {
    println!("Spread not available (non-binary market)");
}
The spread field is pre-computed by each exchange from bid/ask data when available. The spread() method computes it from outcome prices as a fallback. Returns None for non-binary markets.

get_token_ids()

Get the CLOB token IDs from market metadata (primarily for Polymarket):
let token_ids = market.get_token_ids();
// ["token-id-for-yes", "token-id-for-no"]

for (i, token_id) in token_ids.iter().enumerate() {
    println!("Outcome {}: {}", market.outcomes[i], token_id);
}

get_outcome_tokens()

Get outcome-token pairs for easier handling:
let outcome_tokens = market.get_outcome_tokens();

for ot in outcome_tokens {
    println!("{} -> {}", ot.outcome, ot.token_id);
}
// Yes -> 21742633143463906290569050155826241533067272736897614950488156847949938836455
// No -> 48331043336612883890938759509493159234755048973500640148014422747788308965732
Returns a Vec<OutcomeToken>:
pub struct OutcomeToken {
    pub outcome: String,
    pub token_id: String,
}

Examples

Fetch and Display Market

use pc_core::{Exchange, FetchMarketsParams};
use pc_exchange_polymarket::{Polymarket, PolymarketConfig};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let exchange = Polymarket::new(PolymarketConfig::new())?;

    let markets = exchange
        .fetch_markets(Some(FetchMarketsParams {
            limit: Some(5),
            active_only: true,
            ..Default::default()
        }))
        .await?;

    for market in markets {
        println!("ID:       {}", market.id);
        println!("Question: {}", market.question);
        println!("Exchange: {:?}", market.exchange);
        println!("Category: {:?}", market.category);
        println!("Status:   {:?}", market.status);

        // Display outcomes and prices
        for outcome in &market.outcomes {
            if let Some(price) = market.prices.get(outcome) {
                println!("  {}: {:.1}%", outcome, price * 100.0);
            }
        }

        // Display spread
        if let Some(spread) = market.spread {
            println!("Spread:   {:.2}%", spread * 100.0);
        }

        // Display volume metrics
        println!("Volume:    ${:,.0}", market.volume);
        if let Some(v24) = market.volume_24h {
            println!("24h Vol:   ${:,.0}", v24);
        }
        if let Some(avg) = market.daily_avg_volume {
            println!("Avg Daily: ${:,.0}", avg);
        }
        if let Some(oi) = market.open_interest {
            println!("Open Int:  ${:,.0}", oi);
        }
    }

    Ok(())
}

Fetch Markets with Category Filter

use pc_core::{Exchange, FetchMarketsParams};

let markets = exchange
    .fetch_markets(Some(FetchMarketsParams {
        limit: Some(10),
        active_only: true,
        category: Some("crypto".to_string()),
        keyword: Some("crypto".to_string()),
    }))
    .await?;

Working with Token IDs

// Fetch a specific market
let market = exchange.fetch_market("condition-id").await?;

// Token IDs are now directly available
if let Some(ref ids) = market.token_ids {
    for (i, token_id) in ids.iter().enumerate() {
        println!("{}: {}", market.outcomes[i], token_id);
    }
}

// Or use the legacy method via metadata
let tokens = market.get_outcome_tokens();
let yes_token = tokens.iter()
    .find(|t| t.outcome == "Yes")
    .expect("Yes outcome not found");

println!("Yes token ID: {}", yes_token.token_id);

Filter Markets by Criteria

let markets = exchange.fetch_markets(None).await?;

// Filter for binary markets with high volume
let high_volume_binary: Vec<_> = markets
    .iter()
    .filter(|m| m.is_binary() && m.volume > 100_000.0)
    .collect();

// Filter for tight spreads
let tight_spread: Vec<_> = markets
    .iter()
    .filter(|m| m.spread.map_or(false, |s| s < 0.03))
    .collect();

// Filter for open markets closing soon
let closing_soon: Vec<_> = markets
    .iter()
    .filter(|m| {
        if let Some(close_time) = m.close_time {
            let hours_until_close = (close_time - Utc::now()).num_hours();
            m.is_open() && hours_until_close < 24 && hours_until_close > 0
        } else {
            false
        }
    })
    .collect();

Metadata

The metadata field contains exchange-specific data as JSON. Common fields include:

Polymarket

// Access Polymarket-specific metadata
if let Some(obj) = market.metadata.as_object() {
    // CLOB token IDs
    if let Some(tokens) = obj.get("clobTokenIds") {
        println!("Token IDs: {:?}", tokens);
    }

    // Condition ID
    if let Some(condition_id) = obj.get("conditionId") {
        println!("Condition ID: {}", condition_id);
    }

    // Market slug
    if let Some(slug) = obj.get("slug") {
        println!("Slug: {}", slug);
    }
}

Kalshi

// Access Kalshi-specific metadata
if let Some(obj) = market.metadata.as_object() {
    // Ticker
    if let Some(ticker) = obj.get("ticker") {
        println!("Ticker: {}", ticker);
    }

    // Event ID
    if let Some(event_id) = obj.get("eventId") {
        println!("Event ID: {}", event_id);
    }
}

Serialization

Markets can be serialized/deserialized using serde. Optional fields with None values are omitted from JSON output:
use serde_json;

// Serialize to JSON
let json = serde_json::to_string_pretty(&market)?;
println!("{}", json);

// Deserialize from JSON
let market: Market = serde_json::from_str(&json)?;

Next Steps