# 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](https://docs.ojolabs.xyz/markets/api/programmatic-trading/broken-reference) 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)
```
