# fireplace-sdk

Python SDK for the [Fireplace](https://pro.fireplace.gg) External API — prediction-market data, wallet analytics, and real-time WebSocket streams.

```bash
pip install fireplace-sdk
```

## Quick Start

```python
from fireplace_gg import Fireplace

fp = Fireplace(
    api_key="fp_pk_your_public_key",
    api_secret="fp_sk_your_secret_key",
)

# Search for events
events = fp.search.events(q="Bitcoin", limit=5)

# Get market metadata
market = fp.markets.get_by_id("12345")

# Top traders this week
leaders = fp.leaderboard.get(timeframe="7d", sort_by="total_pnl", limit=25)
```

## Authentication

Get your API key pair from the Fireplace team. Pass them directly or set environment variables:

```bash
export FIREPLACE_API_KEY=fp_pk_your_public_key
export FIREPLACE_API_SECRET=fp_sk_your_secret_key
```

```python
fp = Fireplace()  # reads from env vars automatically
```

---

## API Reference

All parameters marked **Required** are positional. Everything else is keyword-only and optional unless noted.

---

### `fp.markets` — Market Data

#### `fp.markets.get_by_id(id)`

Get market metadata by ID.

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | `str` | Market ID. **Required.** |

#### `fp.markets.get_event_by_id(id, *, show_active)`

Get event metadata and its child markets.

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | `str` | Event ID. **Required.** |
| `show_active` | `bool` | Only return active markets within the event. |

#### `fp.markets.get_by_asset_id(asset_ids)`

Get markets by CLOB token asset IDs.

| Parameter | Type | Description |
|-----------|------|-------------|
| `asset_ids` | `str \| list[str]` | One or more CLOB token asset IDs. **Required.** |

#### `fp.markets.overview(market_id)`

Comprehensive market overview — current prices, volume, holder counts, and metadata.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_id` | `str` | Market ID. **Required.** |

#### `fp.markets.latest_candles(market_ids)`

Latest OHLCV candle for one or more markets.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs (max 100). **Required.** |

#### `fp.markets.historical_candles(market_ids, *, outcome, start_time, end_time, interval, include_orderbook, limit, all)`

Historical OHLCV candles with configurable interval.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |
| `outcome` | `str` | `"OUT1"` or `"OUT2"`. |
| `start_time` | `int` | Unix timestamp (seconds). |
| `end_time` | `int` | Unix timestamp (seconds). |
| `interval` | `str` | `"1m"`, `"5m"`, `"15m"`, `"1h"`, `"4h"`, `"1d"`. |
| `include_orderbook` | `bool` | Include bid/offer liquidity depth. |
| `limit` | `int` | Max candles to return. |
| `all` | `bool` | Return all candles in the time range. |

#### `fp.markets.recent_trades(market_ids, *, limit, cursor, outcome, side, min_amount)`

Recent trades for given markets with cursor-based pagination.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |
| `limit` | `int` | Max results. |
| `cursor` | `str` | Pagination cursor (`blockNumber:txIndex:logIndex`). |
| `outcome` | `str` | Filter by outcome. |
| `side` | `str` | `"BUY"` or `"SELL"`. |
| `min_amount` | `float` | Minimum trade amount in USD. |

#### `fp.markets.trades_summary(market_ids, *, outcome, side, min_amount)`

Aggregated trade statistics (count, volume, avg price).

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |
| `outcome` | `str` | Filter by outcome. |
| `side` | `str` | `"BUY"` or `"SELL"`. |
| `min_amount` | `float` | Minimum trade amount. |

#### `fp.markets.get_volume(market_id, *, timescale)`

Trading volume for a market over a given timescale.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `market_id` | `str` | — | Market ID. **Required.** |
| `timescale` | `str` | `"ALL"` | `"1H"`, `"1D"`, `"1W"`, `"1M"`, `"1Y"`, or `"ALL"`. |

#### `fp.markets.open_interest(market_id)`

Current open interest for a single market.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_id` | `str` | Market ID. **Required.** |

#### `fp.markets.group_open_interest(event_id)`

Combined open interest across all markets in an event.

| Parameter | Type | Description |
|-----------|------|-------------|
| `event_id` | `str` | Event ID. **Required.** |

#### `fp.markets.orderbook(market_id)`

Live orderbook (bids/asks) for all outcomes of a market.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_id` | `str` | Market ID. **Required.** |

#### `fp.markets.top_positions(market_id, *, limit, offset, outcome, amount)`

Top traders in a market ranked by position size.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_id` | `str` | Market ID. **Required.** |
| `limit` | `int` | 1-1000 (default 50). |
| `offset` | `int` | Default 0. |
| `outcome` | `str` | Filter by outcome. |
| `amount` | `str` | Min position: `"100"`, `"1k"`, `"10k"`. |

#### `fp.markets.top_positions_stats(market_ids)`

Aggregated trader statistics (total holders, total value, etc.).

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |

#### `fp.markets.top_positions_by_event(event_id, *, limit, offset, outcome, amount)`

Top traders across all markets in an event.

| Parameter | Type | Description |
|-----------|------|-------------|
| `event_id` | `str` | Event ID. **Required.** |
| `limit` | `int` | Max results. |
| `offset` | `int` | Default 0. |
| `outcome` | `str` | Filter by outcome. |
| `amount` | `str` | Min position: `"100"`, `"1k"`, `"10k"`. |

#### `fp.markets.top_holders_stats(market_id, *, limit, all_holders_summary)`

Holder statistics and breakdown by outcome.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_id` | `str` | Market ID. **Required.** |
| `limit` | `int` | Max results. |
| `all_holders_summary` | `bool` | Include full summary stats for all holders. |

#### `fp.markets.unique_holders(market_ids, *, outcome, limit, offset, stats)`

Unique holders for given markets with optional per-holder stats.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |
| `outcome` | `str` | Filter by outcome. |
| `limit` | `int` | Number of holders. |
| `offset` | `int` | Pagination offset. |
| `stats` | `bool` | Include detailed stats per holder. |

#### `fp.markets.holders_summary(market_ids)`

Holder counts by market and outcome.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |

---

### `fp.profiles` — Trader Profiles

#### `fp.profiles.overview(trader_address)`

Portfolio overview — total PnL, position value, win rate, volume traded, cash balance.

| Parameter | Type | Description |
|-----------|------|-------------|
| `trader_address` | `str` | Wallet address. **Required.** |

#### `fp.profiles.positions(trader_address, *, limit, offset, is_active, market_id, sort_by, sort_type)`

Trader's positions (active or closed).

| Parameter | Type | Description |
|-----------|------|-------------|
| `trader_address` | `str` | Wallet address. **Required.** |
| `limit` | `int` | Defaults to all for active, 20 for closed. |
| `offset` | `int` | Default 0. |
| `is_active` | `bool` | Filter active/inactive positions. |
| `market_id` | `str` | Filter by market. |
| `sort_by` | `str` | `"bought_value"`, `"sold_value"`, `"avg_entry_price"`, `"current_holdings"`, `"current_price"`, `"pnl"`, `"last_updated"`. |
| `sort_type` | `str` | `"asc"` or `"desc"`. |

#### `fp.profiles.recent_trades(trader_address, *, limit, cursor, market_id, outcome, side, min_shares, min_usd, date_from, date_to)`

Recent trades, redemptions, splits, and merges for a trader.

| Parameter | Type | Description |
|-----------|------|-------------|
| `trader_address` | `str` | Wallet address. **Required.** |
| `limit` | `int` | 1-1000 (default 50). |
| `cursor` | `str` | Cursor (`blockNumber:txIndex:logIndex`). |
| `market_id` | `str` | Filter by market. |
| `outcome` | `str` | Filter by outcome (e.g. `"Yes"`, `"No"`). Requires `market_id`. |
| `side` | `str` | `"BUY"` or `"SELL"`. |
| `min_shares` | `float` | Minimum shares threshold. |
| `min_usd` | `float` | Minimum USD value threshold. |
| `date_from` | `int` | Start time filter (Unix seconds). |
| `date_to` | `int` | End time filter (Unix seconds). |

#### `fp.profiles.historical_pnl(trader_address, *, time_period)`

Time-series PnL data for charting. Granularity: 1d→5min, 1w→30min, 1m→2hr, 1y/all→12hr.

| Parameter | Type | Description |
|-----------|------|-------------|
| `trader_address` | `str` | Wallet address. **Required.** |
| `time_period` | `str` | `"1d"`, `"1w"`, `"1m"`, `"1y"`, `"all"`. |

#### `fp.profiles.activity(trader_address, *, limit, cursor)`

Account activity feed (deposits, withdrawals, maker rebates, fee refunds).

| Parameter | Type | Description |
|-----------|------|-------------|
| `trader_address` | `str` | Wallet address. **Required.** |
| `limit` | `int` | 1-1000 (default 50). |
| `cursor` | `str` | Pagination cursor (`blockNumber:txIndex:logIndex`). |

#### `fp.profiles.unredeemed_positions(trader_address)`

Unredeemed winning positions on resolved markets.

| Parameter | Type | Description |
|-----------|------|-------------|
| `trader_address` | `str` | Wallet address. **Required.** |

---

### `fp.leaderboard` — Trader Leaderboard

#### `fp.leaderboard.get(*, categories, timeframe, sort_by, sort_order, limit, offset, traded_in, ...)`

Paginated trader leaderboard with extensive filtering.

| Parameter | Type | Description |
|-----------|------|-------------|
| `categories` | `str` | Comma-separated (e.g. `"Politics,Sports"`). `"all"` or omit for no filter. |
| `timeframe` | `str` | `"1d"`, `"7d"`, `"30d"`, `"all"` (aliases: `"today"`, `"weekly"`, `"monthly"`). |
| `sort_by` | `str` | `"total_pnl"`, `"volume_usd"`, `"win_rate"`, `"trade_count"`, `"roi"`, etc. |
| `sort_order` | `str` | `"asc"` or `"desc"`. |
| `limit` | `int` | 1-1000 (default 100). |
| `offset` | `int` | Default 0. |
| `traded_in` | `str` | JSON array `[{eventId, marketIds[]}]` to filter by market activity. |
| `min_volume` / `max_volume` | `float` | Volume USD filter. |
| `min_trades` / `max_trades` | `int` | Trade count filter. |
| `min_win_rate` / `max_win_rate` | `float` | Win rate filter. |
| `min_roi` / `max_roi` | `float` | ROI filter. |
| `min_pnl` / `max_pnl` | `float` | Total PnL filter. |
| `min_realized_pnl` / `max_realized_pnl` | `float` | Realized PnL filter. |
| `min_unrealized_pnl` / `max_unrealized_pnl` | `float` | Unrealized PnL filter. |
| `min_avg_entry_price` / `max_avg_entry_price` | `float` | Avg entry price filter. |
| `min_avg_trade_size` / `max_avg_trade_size` | `float` | Avg trade size filter. |
| `min_trades_per_day` / `max_trades_per_day` | `float` | Trades per day filter. |
| `min_markets_traded` / `max_markets_traded` | `int` | Markets traded filter. |

#### `fp.leaderboard.search(*, q, traded_in, categories, timeframe, sort_by, sort_order, limit, offset, ...)`

Search traders by wallet address, username, pseudonym, or X handle.

| Parameter | Type | Description |
|-----------|------|-------------|
| `q` | `str` | Search query. Required if no `traded_in`. |
| `traded_in` | `str` | JSON array `[{eventId, marketIds[]}]`. Required if no `q`. |
| `categories` | `str` | Category filter. |
| `timeframe` | `str` | `"1d"`, `"7d"`, `"30d"`, `"all"`. |
| `sort_by` | `str` | Sort field. |
| `sort_order` | `str` | `"asc"` or `"desc"`. |
| `limit` | `int` | Number of results. |
| `offset` | `int` | Pagination offset. |
| `min_volume` / `max_volume` | `float` | Volume filter. |
| `min_trades` / `max_trades` | `int` | Trade count filter. |
| `min_win_rate` / `max_win_rate` | `float` | Win rate filter. |
| `min_roi` / `max_roi` | `float` | ROI filter. |
| `min_pnl` / `max_pnl` | `float` | PnL filter. |

#### `fp.leaderboard.user(user_address)`

Detailed performance stats for a single trader across all categories and timeframes.

| Parameter | Type | Description |
|-----------|------|-------------|
| `user_address` | `str` | Ethereum wallet address. **Required.** |

#### `fp.leaderboard.categories()`

Returns the list of all available leaderboard categories. No parameters.

---

### `fp.search` — Market & Event Search

#### `fp.search.markets(*, q, limit, offset, sort_by, sort_order, status, category, categories, ...)`

Elasticsearch-powered market search with volume/price/volatility filters.

| Parameter | Type | Description |
|-----------|------|-------------|
| `q` | `str` | Text search query. |
| `limit` | `int` | Max results. |
| `offset` | `int` | Pagination offset. |
| `sort_by` | `str` | Sort field (e.g. `"volume_24h"`, `"price"`, `"end_date"`). |
| `sort_order` | `str` | `"asc"` or `"desc"`. |
| `status` | `str` | `"active"` or `"closed"`. |
| `category` | `str` | Single category filter. |
| `categories` | `str` | Multiple categories (comma-separated). |
| `volume_min` / `volume_max` | `float` | 24-hour combined volume filter. |
| `price_min` / `price_max` | `float` | Price filter (0-1). |
| `expires_in_seconds` | `int` | Markets closing within N seconds. |
| `opened_seconds_back` | `int` | Markets opened within this many seconds ago. |
| `min_volume_1h` / `max_volume_1h` | `float` | 1-hour volume filter. |
| `min_volume_1d` / `max_volume_1d` | `float` | 24-hour volume filter. |
| `min_price_change_1h` / `max_price_change_1h` | `float` | 1h price change (%). |
| `min_price_change_1d` / `max_price_change_1d` | `float` | 24h price change (%). |
| `min_total_volume` / `max_total_volume` | `float` | All-time total volume filter. |
| `holding_rewards_enabled` | `bool` | Filter to markets with holding rewards enabled. |
| `recurrence` | `str` | Filter by recurrence type (e.g. `"None"` to exclude recurring markets). |
| `available_bids` | `bool` | Filter to markets with active bids on the orderbook. |
| `available_offers` | `bool` | Filter to markets with active offers on the orderbook. |

#### `fp.search.events(*, q, limit, offset, sort_by, sort_order, status, category, categories)`

Search events (grouped markets). Same idea as `markets()` but results are grouped by event.

| Parameter | Type | Description |
|-----------|------|-------------|
| `q` | `str` | Text search query. |
| `limit` | `int` | Max results. |
| `offset` | `int` | Pagination offset. |
| `sort_by` | `str` | Sort field. |
| `sort_order` | `str` | `"asc"` or `"desc"`. |
| `status` | `str` | `"active"` or `"closed"`. |
| `category` | `str` | Single category filter. |
| `categories` | `str` | Multiple categories (comma-separated). |

---

### `fp.wallets` — Wallet Analytics

#### `fp.wallets.trades(wallets, *, limit, cursor, market_id, outcome, side, min_amount, min_price, max_price, start_time, end_time)`

Recent trades for up to 50 wallet addresses.

| Parameter | Type | Description |
|-----------|------|-------------|
| `wallets` | `str \| list[str]` | Wallet addresses (1-50). **Required.** |
| `limit` | `int` | 1-100 (default 50). |
| `cursor` | `str` | Pagination cursor (`blockNumber:txIndex:logIndex`). |
| `market_id` | `str` | Filter by market. |
| `outcome` | `str` | Filter by outcome. Requires `market_id`. |
| `side` | `str` | `"BUY"` or `"SELL"`. |
| `min_amount` | `float` | Minimum trade amount in USD. |
| `min_price` | `float` | Minimum price (0-1). |
| `max_price` | `float` | Maximum price (0-1). |
| `start_time` | `str` | Start time (ISO 8601). |
| `end_time` | `str` | End time (ISO 8601). |

#### `fp.wallets.net_flows(wallets, *, limit, offset, sort_by, sort_order, lookback)`

Net buy/sell flows aggregated by market — shows which markets wallets are accumulating vs distributing.

| Parameter | Type | Description |
|-----------|------|-------------|
| `wallets` | `str \| list[str]` | Wallet addresses (1-50). **Required.** |
| `limit` | `int` | 1-100 (default 20). |
| `offset` | `int` | Default 0. |
| `sort_by` | `str` | `"volume"`, `"buyVolume"`, `"sellVolume"`, `"netFlows"` (default). |
| `sort_order` | `str` | `"asc"` or `"desc"` (default `"desc"`). |
| `lookback` | `str` | `"Nd"` for days (1-30), `"Nh"` for hours (1-23). Omit for all-time. |

---

### `fp.stream` — Real-Time WebSocket

All streaming methods are async iterators. Requires `websockets` (installed with the SDK).

```python
import asyncio
from fireplace_gg import Fireplace

fp = Fireplace(api_key="fp_pk_...", api_secret="fp_sk_...")

async def main():
    async for msg in fp.stream.trades_by_market(["12345"]):
        print(msg)

asyncio.run(main())
```

#### `fp.stream.trades_by_market(market_ids)`

Stream real-time trades for a list of markets.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |

#### `fp.stream.trades_by_wallet(wallets)`

Stream real-time trades involving specific wallets.

| Parameter | Type | Description |
|-----------|------|-------------|
| `wallets` | `str \| list[str]` | Wallet addresses. **Required.** |

#### `fp.stream.market_headers(market_ids)`

Stream market metadata updates (price, volume, liquidity changes).

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |

#### `fp.stream.candles(market_ids)`

Stream real-time OHLCV candle updates.

| Parameter | Type | Description |
|-----------|------|-------------|
| `market_ids` | `str \| list[str]` | Market IDs. **Required.** |

---

## Error Handling

```python
from fireplace_gg import (
    Fireplace,
    AuthenticationError,   # 401
    ForbiddenError,        # 403
    RateLimitError,        # 429
    BadRequestError,       # 400
    ServerError,           # 5xx
    FireplaceError,        # base class
)

try:
    market = fp.markets.get_by_id("12345")
except RateLimitError as e:
    print(f"Rate limited — retry in {e.retry_after:.1f}s")
except AuthenticationError:
    print("Invalid API keys")
```

## Rate Limits

Default: **5 requests per second** (sliding window). After any request:

```python
fp.rate_remaining  # requests left in current window
fp.rate_reset      # unix timestamp when window resets
fp.rate_limit      # max requests per window
```

## License

MIT
