> For the complete documentation index, see [llms.txt](https://docs.ojolabs.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ojolabs.xyz/markets/api/programmatic-trading/market-data.md).

# Market Data

Guide for fetching and streaming market data programmatically using the Python SDK.

## Prerequisites

Market data endpoints are public — no authentication required:

```python
from turbine_client import TurbineClient, Outcome

client = TurbineClient(
    host="https://api.turbinefi.com",
    chain_id=137,
)
```

## List Markets

Fetch all markets, optionally filtered by chain:

```python
# All markets
markets = client.get_markets()

# Filter by chain
markets = client.get_markets(chain_id=137)

for m in markets:
    status = "RESOLVED" if m.resolved else "ACTIVE"
    print(f"[{status}] {m.question}")
    print(f"  ID: {m.id}")
    print(f"  Contract: {m.contract_address}")
    print(f"  Volume: {m.volume / 1e6:.2f} USDC")
    print()
```

Each `Market` object contains the market ID (used for API calls) and the contract address (used for claiming winnings). See [Types](broken://pages/40cef22ae56a28e3082475fb5925e8bbe5a677fd#market) for all fields.

## Orderbook Snapshots

Get the current orderbook for a market:

```python
market_id = "0x..."

# Full orderbook (both outcomes)
ob = client.get_orderbook(market_id)

# YES side only
yes_ob = client.get_orderbook(market_id, outcome=Outcome.YES)

# NO side only
no_ob = client.get_orderbook(market_id, outcome=Outcome.NO)
```

### Parsing the Orderbook

Bids are sorted highest-first (best bid on top). Asks are sorted lowest-first (best ask on top).

```python
ob = client.get_orderbook(market_id, outcome=Outcome.YES)

print("YES Orderbook:")
print(f"{'BIDS':>40} | ASKS")
print("-" * 80)

max_levels = max(len(ob.bids), len(ob.asks))
for i in range(min(max_levels, 5)):
    bid_str = ""
    ask_str = ""

    if i < len(ob.bids):
        b = ob.bids[i]
        bid_str = f"{b.size / 1e6:>8.2f} @ {b.price:>6} (${b.price / 1e6:.4f})"

    if i < len(ob.asks):
        a = ob.asks[i]
        ask_str = f"{a.price:>6} (${a.price / 1e6:.4f}) x {a.size / 1e6:.2f}"

    print(f"{bid_str:>40} | {ask_str}")
```

### Derived Metrics

```python
ob = client.get_orderbook(market_id, outcome=Outcome.YES)

if ob.bids and ob.asks:
    best_bid = ob.bids[0].price
    best_ask = ob.asks[0].price
    mid = (best_bid + best_ask) / 2
    spread = best_ask - best_bid
    spread_pct = spread / mid * 100 if mid > 0 else 0

    print(f"Best bid: {best_bid} (${best_bid / 1e6:.4f})")
    print(f"Best ask: {best_ask} (${best_ask / 1e6:.4f})")
    print(f"Mid:      {mid:.0f} (${mid / 1e6:.4f})")
    print(f"Spread:   {spread} ({spread_pct:.2f}%)")

    # Total depth
    bid_depth = sum(level.size for level in ob.bids) / 1e6
    ask_depth = sum(level.size for level in ob.asks) / 1e6
    print(f"Bid depth: {bid_depth:.2f} shares")
    print(f"Ask depth: {ask_depth:.2f} shares")
```

## Trade History

Get recent trades for a market:

```python
trades = client.get_trades(market_id, limit=20)

for t in trades:
    outcome = "YES" if t.outcome == 0 else "NO"
    print(
        f"{outcome} {t.size / 1e6:.2f} "
        f"@ {t.price} (${t.price / 1e6:.4f}) | "
        f"tx={t.tx_hash[:16]}..."
    )
```

### Aggregate Trade Data

```python
trades = client.get_trades(market_id, limit=100)

if trades:
    prices = [t.price for t in trades]
    sizes = [t.size for t in trades]
    total_volume = sum(t.price * t.size // 1_000_000 for t in trades)

    print(f"Trades: {len(trades)}")
    print(f"Price range: {min(prices)} (${min(prices) / 1e6:.4f}) — {max(prices)} (${max(prices) / 1e6:.4f})")
    print(f"VWAP: {sum(t.price * t.size for t in trades) // sum(sizes)} (${sum(t.price * t.size for t in trades) / sum(sizes) / 1e6:.4f})")
    print(f"Volume: {total_volume / 1e6:.2f} USDC")
```

## Market Statistics

### Single Market

```python
stats = client.get_market(market_id)

print(f"Last price: {stats.last_price} (${stats.last_price / 1e6:.4f})")
print(f"24h volume: {stats.volume_24h / 1e6:.2f} USDC")
print(f"Total volume: {stats.total_volume / 1e6:.2f} USDC")
```

### Platform-Wide

```python
platform = client.get_platform_stats()

print(f"Total volume: {platform.total_volume / 1e6:.2f} USDC")
print(f"Total trades: {platform.total_trades}")

for chain in platform.chains:
    print(f"  Chain {chain.chain_id}: {chain.total_volume / 1e6:.2f} USDC, {chain.total_trades} trades")
```

### Top Holders

```python
holders = client.get_holders(market_id, limit=10)

for i, h in enumerate(holders, 1):
    print(
        f"#{i} {h.user_address[:10]}... | "
        f"YES={h.yes_shares / 1e6:.2f} | "
        f"NO={h.no_shares / 1e6:.2f} | "
        f"Invested={h.total_invested / 1e6:.2f} USDC"
    )
```

## Quick Market Data

Quick markets are 15-minute prediction markets for BTC and ETH. They rotate automatically.

### Active Quick Market

```python
qm = client.get_quick_market("BTC")

print(f"Question: Will BTC be above ${qm.start_price / 1e8:,.2f}?")
print(f"Market ID: {qm.market_id}")
print(f"Contract: {qm.contract_address}")
print(f"Starts: {qm.start_time}")
print(f"Ends: {qm.end_time}")
print(f"Resolved: {qm.resolved}")
```

### Current Price

```python
price = client.get_quick_market_price("BTC")
print(f"BTC: ${price.price / 1e8:,.2f}")
```

### Price History

```python
history = client.get_quick_market_price_history("BTC", limit=60)

for p in history:
    print(f"  {p.timestamp}: ${p.price / 1e8:,.2f}")
```

### Quick Market History

```python
past_markets = client.get_quick_market_history("BTC", limit=10)

for qm in past_markets:
    if qm.resolved:
        outcome = "YES" if qm.outcome == 0 else "NO"
        print(f"  Strike: ${qm.start_price / 1e8:,.2f} → End: ${qm.end_price / 1e8:,.2f} = {outcome}")
    else:
        print(f"  Strike: ${qm.start_price / 1e8:,.2f} (active)")
```

## Real-Time Streaming via WebSocket

For live data, use the `TurbineWSClient`:

```python
import asyncio
from turbine_client.ws import TurbineWSClient

async def stream_market(market_id: str):
    ws = TurbineWSClient(host="https://api.turbinefi.com")

    async with ws.connect() as stream:
        await stream.subscribe(market_id)
        print(f"Subscribed to {market_id}")

        async for msg in stream:
            if msg.type == "orderbook":
                ob = msg.orderbook
                if ob and ob.bids and ob.asks:
                    mid = (ob.bids[0].price + ob.asks[0].price) / 2
                    spread = ob.asks[0].price - ob.bids[0].price
                    print(f"Book: {ob.bids[0].price}/{ob.asks[0].price} mid={mid:.0f} spread={spread}")

            elif msg.type == "trade":
                trade = msg.trade
                if trade:
                    outcome = "YES" if trade.outcome == 0 else "NO"
                    print(f"Trade: {trade.size / 1e6:.2f} {outcome} @ {trade.price} (${trade.price / 1e6:.4f})")

asyncio.run(stream_market("0x..."))
```

### Multi-Market Streaming

```python
async def stream_multiple():
    client = TurbineClient(host="https://api.turbinefi.com", chain_id=137)

    # Get active quick markets
    btc_market = client.get_quick_market("BTC")
    eth_market = client.get_quick_market("ETH")

    ws = TurbineWSClient(host="https://api.turbinefi.com")

    async with ws.connect() as stream:
        await stream.subscribe(btc_market.market_id)
        await stream.subscribe(eth_market.market_id)

        async for msg in stream:
            if msg.type == "trade":
                trade = msg.trade
                if trade:
                    # Identify which market
                    asset = "BTC" if msg.market_id == btc_market.market_id else "ETH"
                    outcome = "YES" if trade.outcome == 0 else "NO"
                    print(f"[{asset}] {outcome} {trade.size / 1e6:.2f} @ {trade.price} (${trade.price / 1e6:.4f})")

asyncio.run(stream_multiple())
```

### Market Transition Detection

Quick markets rotate every 15 minutes. Detect transitions via WebSocket:

```python
async def stream_with_transitions():
    client = TurbineClient(host="https://api.turbinefi.com", chain_id=137)
    ws = TurbineWSClient(host="https://api.turbinefi.com")

    qm = client.get_quick_market("BTC")
    current_market_id = qm.market_id

    async with ws.connect() as stream:
        await stream.subscribe(current_market_id)

        async for msg in stream:
            if msg.type == "quick_market":
                new_qm = msg.quick_market
                if new_qm and new_qm.market_id != current_market_id:
                    # Switch to new market
                    await stream.unsubscribe(current_market_id)
                    current_market_id = new_qm.market_id
                    await stream.subscribe(current_market_id)
                    print(f"New market: {current_market_id} (strike: ${new_qm.start_price / 1e8:,.2f})")

            elif msg.type == "orderbook":
                ob = msg.orderbook
                if ob and ob.bids and ob.asks:
                    print(f"  {ob.bids[0].price}/{ob.asks[0].price}")

asyncio.run(stream_with_transitions())
```

## Complete Example

A market data dashboard that polls and displays key metrics:

```python
import os
import time
from turbine_client import TurbineClient, Outcome

client = TurbineClient(host="https://api.turbinefi.com", chain_id=137)

# Get BTC quick market
qm = client.get_quick_market("BTC")
market_id = qm.market_id

print(f"Market: Will BTC be above ${qm.start_price / 1e8:,.2f}?")
print(f"Ends: {qm.end_time}")
print()

while True:
    # Current BTC price
    btc = client.get_quick_market_price("BTC")
    above_strike = btc.price > qm.start_price

    # Orderbook
    yes_ob = client.get_orderbook(market_id, outcome=Outcome.YES)
    no_ob = client.get_orderbook(market_id, outcome=Outcome.NO)

    print(f"BTC: ${btc.price / 1e8:,.2f} ({'ABOVE' if above_strike else 'BELOW'} strike)")

    if yes_ob.bids and yes_ob.asks:
        yes_mid = (yes_ob.bids[0].price + yes_ob.asks[0].price) / 2
        print(f"  YES: {yes_ob.bids[0].price}/{yes_ob.asks[0].price} (mid: ${yes_mid / 1e6:.4f})")

    if no_ob.bids and no_ob.asks:
        no_mid = (no_ob.bids[0].price + no_ob.asks[0].price) / 2
        print(f"  NO:  {no_ob.bids[0].price}/{no_ob.asks[0].price} (mid: ${no_mid / 1e6:.4f})")

    # Recent trades
    trades = client.get_trades(market_id, limit=3)
    if trades:
        print(f"  Last trades:")
        for t in trades:
            outcome = "YES" if t.outcome == 0 else "NO"
            print(f"    {outcome} {t.size / 1e6:.2f} @ {t.price} (${t.price / 1e6:.4f})")

    # Check if market expired
    remaining = qm.end_time - int(time.time())
    if remaining <= 0:
        print("\nMarket expired. Fetching new market...")
        qm = client.get_quick_market("BTC")
        market_id = qm.market_id
        print(f"New strike: ${qm.start_price / 1e8:,.2f}")
    else:
        print(f"  Time left: {remaining // 60}m {remaining % 60}s")

    print()
    time.sleep(10)
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ojolabs.xyz/markets/api/programmatic-trading/market-data.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
