npm package: @polybased/sdk
Description: Complete TypeScript SDK for Polymarket — fetch market data, stream live orderbooks, and execute trades with CLOB integration for trading bots, dashboards, and AI agents.
Author: Ollie Grimes
Latest version: 0.9.0

# Polybased SDK

**Polybased SDK** is a complete TypeScript toolkit for **Polymarket** — fetch real-time data, stream live orderbooks, and **execute trades** programmatically.

Build automated trading bots, market-making strategies, dashboards, and AI agents powered by on-chain prediction markets.

[![npm version](https://img.shields.io/npm/v/@polybased/sdk.svg)](https://www.npmjs.com/package/@polybased/sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

---

## ✨ Features 

| Category | Features |
|----------|----------|
| **📊 Market Data** | Fetch markets, search, real-time orderbooks, historical prices |
| **⚡ WebSocket Streaming** | Live orderbooks, prices, trades with auto-reconnect |
| **🔄 Multi-Market** | Monitor multiple markets with ONE efficient connection |
| **💰 Trading** | All order types: GTC, GTD, FOK, FAK + market orders |
| **📈 Price History** | Historical prices with intervals: max, 1w, 1d, 6h, 1h |
| **🏆 Rewards** | Order scoring, earnings tracking, liquidity rewards |
| **🔬 AI Research** | AI-powered insights via FactsAI |
| **🔄 Gasless Trades** | Relayer support for meta-transactions |
| **🏗️ Builder Support** | Order attribution for analytics & rewards |
| **🔒 Type-Safe** | Full TypeScript with comprehensive types |
| **🧠 Smart Money** | Track top traders, identify smart money activity |
| **📊 Arbitrage** | Detect AND auto-execute arbitrage opportunities |
| **📈 Market Signals** | Volume spikes, momentum, whale activity detection |
| **🚦 Rate Limiting** | Token bucket algorithm prevents API bans |
| **📋 Copy Trading** | Follow and copy smart money trades |
| **⛓️ On-Chain** | Balance queries, approvals, CTF split/merge/redeem |
| **🌉 Bridge API** | Cross-chain deposits (EVM, Solana, Bitcoin) |
| **📊 Subgraph** | 5 Goldsky subgraphs for on-chain data queries |

### 🆕 New in v0.9.0

- ✅ **Bridge API Client** — Cross-chain deposits from EVM, Solana, Bitcoin
- ✅ **Subgraph Client** — Query 5 Goldsky subgraphs (positions, orderbook, activity, OI, PnL)
- ✅ **CTF Operations** — Split, Merge, Redeem, RedeemNegRisk on-chain
- ✅ **Gamma API** — Tags, comments, public-search, series, profiles, sports
- ✅ **Data API** — Closed positions, holders, open interest, volume, builder leaderboard
- ✅ **New Types** — 11 new TypeScript types for WebSocket events, CTF, Bridge, Subgraph

### v0.8.1 Features

- ✅ **Arbitrage Executor** — Auto-execute arbitrage opportunities
- ✅ **Copy Trading** — Follow and copy smart money trades
- ✅ **On-Chain Service** — Balance queries, approvals, trading readiness
- ✅ **Event System** — Subscribe to executor/copier events

### v0.8.0 Features

- ✅ **Smart Money Analytics** — Track top traders, identify smart money
- ✅ **Arbitrage Detection** — Find long/short arbitrage opportunities
- ✅ **Market Signals** — Volume spikes, momentum, whale activity
- ✅ **Rate Limiting** — Token bucket algorithm prevents API bans
- ✅ **Unified Caching** — TTL-based caching with LRU eviction
- ✅ **Enhanced Logging** — Structured logging with levels
- ✅ **Improved Errors** — Context-rich errors with hints
- ✅ **Authenticated WebSocket** — Real-time user data (orders, trades)
- ✅ **Unit Tests** — Comprehensive test coverage with Vitest

### Previous Releases

**v0.7.0:** All Order Types, Market Orders, Gamma API, Data API, K-Lines, Technical Indicators, 100% API Coverage

> **This SDK is a complete trading solution** — not just for data. It includes historical data, AI-powered research, live trading, rewards tracking, and gasless transactions!

---

## 🏗️ Architecture Overview

Polybased SDK uses a **3-layer architecture** for maximum flexibility:

```
┌──────────────────────────────────────────────────────────────────────┐
│                          PolybasedSDK                                │
│                        (Main Entry Point)                            │
├──────────────────────────────────────────────────────────────────────┤
│  LAYER 3: HIGH-LEVEL SERVICES (Recommended)                          │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐│
│  │ MarketService│ │TradingService│ │ StreamService│ │ResearchService││
│  │ • getMarkets │ │ • placeOrder │ │ • onOrderbook│ │ • query()    ││
│  │ • search()   │ │ • cancelOrder│ │ • onPrice()  │ │ • healthCheck││
│  └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘│
├──────────────────────────────────────────────────────────────────────┤
│  LAYER 2: API CLIENTS (Advanced users)                               │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐│
│  │ ClobClient   │ │ GammaClient  │ │WebSocketClient│ │ RelayClient  ││
│  └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘│
├──────────────────────────────────────────────────────────────────────┤
│  LAYER 1: CORE (Logger, Retry, Cache, Types, Errors)                 │
└──────────────────────────────────────────────────────────────────────┘
```

📖 **See [ARCHITECTURE.md](./ARCHITECTURE.md) for detailed design documentation.**

---

## 📅 Roadmap

| Version | Target | Key Features | Status |
|---------|--------|--------------|--------|
| **v0.7.0** | Q1 2025 | Full API coverage, Gamma, Data, K-Lines | ✅ Released |
| **v0.8.0** | Q1 2025 | Smart Money, Arbitrage, Signals, Testing | ✅ Released |
| **v0.8.1** | Q1 2025 | Arb Executor, Copy Trading, On-Chain | ✅ Released |
| **v0.9.0** | Q1 2025 | CTF Operations, Bridge API, Subgraph, Full API Coverage | ✅ Released |
| **v1.0.0** | Q3 2025 | Strategy Framework, Full Stability | Planned |

📖 **See [SDK_ROADMAP.md](./SDK_ROADMAP.md) for detailed feature plans.**

---

## 📦 Installation

Install via npm:

```bash
npm install @polybased/sdk
```

Or via yarn:

```bash
yarn add @polybased/sdk
```

For WebSocket support, you may need to install `ws`:

```bash
npm install ws
```

> ⚠️ **Node.js 20.10+** is required for v0.7.0+  
> **Node.js 18+** includes WebSocket support by default - no additional packages needed!  
> **New to this?** Check out [GUIDE_FOR_NEWBIES.md](./GUIDE_FOR_NEWBIES.md) for a complete step-by-step tutorial!

---

## 🚀 Quick Start

### Data & Streaming (No Trading)

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// Fetch all active markets
const markets = await sdk.getMarkets();
console.log(`Found ${markets.length} markets`);

// Get a specific market
const market = await sdk.getMarket("market-id-here");
console.log(market.question);
console.log(market.prices);
```

### Real-time Streaming (WebSocket)

**✨ NEW in v0.5.0:** Enhanced WebSocket support with auto-reconnection, price updates, trade events, and multi-market subscriptions!

```typescript
import { PolybasedSDK } from "@polybased/sdk";

// In Node.js, enable WebSocket support (only if not already available)
if (typeof window === "undefined" && typeof globalThis.WebSocket === "undefined") {
  globalThis.WebSocket = (await import("ws")).default as any;
}

const sdk = new PolybasedSDK({ debug: true });

// 1. Subscribe to orderbook updates
const unsubOrderbook = sdk.onOrderbook(
  "market-id-here",
  (update) => {
    if (update.type === "snapshot") {
      const { bids, asks } = update.data;
      console.log("Best bid:", bids[0]?.price);
      console.log("Best ask:", asks[0]?.price);
    }
  },
  {
    onOpen: () => console.log("WebSocket connected"),
    onClose: () => console.log("WebSocket disconnected"),
    onError: (err) => console.error("WebSocket error:", err)
  }
);

// 2. ✨ NEW: Subscribe to real-time price updates
const unsubPrice = sdk.onPriceUpdate(
  "market-id-here",
  (update) => {
    console.log(`Price: $${update.price}`);
    console.log(`24h Volume: $${update.volume24h}`);
  }
);

// 3. ✨ NEW: Subscribe to real-time trade events
const unsubTrade = sdk.onTrade(
  "market-id-here",
  (trade) => {
    console.log(`${trade.side}: ${trade.size} shares @ $${trade.price}`);
  }
);

// 4. ✨ NEW: Monitor multiple markets efficiently (ONE connection!)
const unsubMulti = sdk.onMultipleMarkets(
  ["market-1", "market-2", "market-3"],
  (update) => {
    console.log(`${update.marketId}: ${update.type}`, update.data);
  },
  {
    onOpen: () => console.log("Multi-market connected"),
    onReconnected: () => console.log("Reconnected!")
  }
);

// 5. ✨ NEW: Dynamic multi-market manager
const manager = sdk.createMultiMarketManager(
  (update) => console.log(`Update from ${update.marketId}`),
  { onOpen: () => console.log("Manager connected") }
);

manager.start();
manager.subscribe(["market-1", "market-2"]); // Add markets
manager.subscribe(["market-3"]); // Add more
manager.unsubscribe(["market-1"]); // Remove markets
manager.stop(); // Cleanup

// Later: unsubscribe from any stream
// unsubOrderbook();
// unsubPrice();
// unsubTrade();
// unsubMulti();
```

**🚀 WebSocket Features (v0.5.0):**
- ✅ Auto-reconnection with exponential backoff
- ✅ Heartbeat/ping to keep connections alive
- ✅ Multiple data types (orderbook, prices, trades)
- ✅ Multi-market subscriptions (90% more efficient!)
- ✅ Dynamic subscription management
- ✅ Production-ready reliability

### Historical Data (Backtesting & Analytics)

**✨ NEW in v0.7.0:** Price history now supports official CLOB intervals!

**Price History Intervals:**
| Interval | Description |
|----------|-------------|
| `max` | All available historical data |
| `1w` | One week of data |
| `1d` | One day of data |
| `6h` | Six hours of data |
| `1h` | One hour of data |

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// ✨ NEW: Get price history with proper intervals
const hourlyPrices = await sdk.history.getPriceHistory({
  market: "condition-id-here",
  interval: "1h"
});

// Get daily prices for last 30 days
const dailyPrices = await sdk.history.getPriceHistory({
  market: "condition-id",
  interval: "1d",
  startTs: Math.floor(Date.now() / 1000) - 30 * 24 * 3600,
  endTs: Math.floor(Date.now() / 1000)
});

// Get all available data
const allPrices = await sdk.history.getPriceHistory({
  market: "condition-id",
  interval: "max"
});

// Each price point: { t: timestamp, p: price }
hourlyPrices.forEach(point => {
  console.log(`${new Date(point.t * 1000).toISOString()}: $${point.p}`);
});

// ✨ Search for markets easily
const btcMarkets = await sdk.searchMarkets("bitcoin");
console.log(`Found ${btcMarkets.length} Bitcoin markets`);

// ✨ Find a specific market
const market = await sdk.findMarket("trump");
console.log(`Market: ${market.question}`);

// ✨ Use market slug with getPrices (auto-resolves!)
const prices = await sdk.history.getPrices("market-slug", {
  interval: "1d"
});

// Or use condition_id directly
const pricesById = await sdk.history.getPrices(market.raw.condition_id);

// Get historical volumes
const volumes = await sdk.history.getVolumes("market-slug");

// Get market snapshot from specific time
const snapshot = await sdk.history.getSnapshot(
  market.raw.condition_id,
  Date.now() - 30 * 24 * 60 * 60 * 1000 // 30 days ago
);

console.log(`Price 30 days ago: $${snapshot.price}`);
console.log(`Volume: $${snapshot.volume}`);

// Backtest a strategy
prices.forEach(point => {
  if (point.price < 0.40) console.log("BUY signal!");
  if (point.price > 0.60) console.log("SELL signal!");
});
```

**💡 Pro Tips:**
- Use **slugs** from Polymarket URLs for convenience
- Use **condition_ids** when you already have them for instant lookup (no resolution needed)
- Use **searchMarkets()** to discover markets by keyword
- First slug resolution takes ~1-2 seconds (fetches all markets), then instant from cache

---

**📚 For Non-Developers: How Slug Resolution Works**

**The Problem:**
Polymarket's API only understands long "condition IDs" like `0x5eed579ff676...` (66 characters). It doesn't accept friendly slugs like `"bitcoin-100k"` directly. If we try to send a slug to the API, it returns a "404 Not Found" error.

**Why We Built Internal Resolution:**
Instead of making you find the condition_id manually (which requires browsing through 1000+ markets), our SDK now does it for you automatically.

**How It Works (Simple Explanation):**
1. **You type**: `getPrices("bitcoin-100k")`
2. **SDK thinks**: "That's not a condition_id, it's a slug!"
3. **SDK fetches**: All markets from Polymarket (one-time)
4. **SDK searches**: Through markets to find `market_slug: "bitcoin-100k"`
5. **SDK finds**: The condition_id for that market
6. **SDK saves**: This mapping in memory (so next time is instant)
7. **SDK uses**: The condition_id to call the API
8. **You get**: Historical prices! ✅

**Why This Is Better:**
- **Before**: You had to search 1000+ markets manually
- **After**: Just use the slug and it works!

**Performance:**
- **First time** using a new slug: ~1-2 seconds (fetches all markets)
- **Every time after**: Instant! (uses cached mapping)
- **Using condition_id directly**: Always instant (no lookup needed)

**Technical Note for Developers:**
The CLOB API endpoint `https://clob.polymarket.com/markets/{slug}` returns 404. Only `https://clob.polymarket.com/markets/{condition_id}` works. We resolve slugs by fetching `/markets`, finding the match in the list, and extracting the `condition_id` field.

---

## 📈 Market Discovery & Analytics (NEW in v0.7.0!)

### Gamma API - Trending Markets & Events

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// Get top 10 trending markets by 24h volume
const trending = await sdk.gamma.getTrendingMarkets(10);
trending.forEach(market => {
  console.log(`${market.question}`);
  console.log(`  Volume 24h: $${market.volume24hr?.toLocaleString()}`);
  console.log(`  Best Bid: ${market.bestBid}`);
});

// Get trending events
const events = await sdk.gamma.getTrendingEvents(5);

// Search markets
const btcMarkets = await sdk.gamma.searchMarkets("bitcoin");

// Get markets by category
const politics = await sdk.gamma.getMarketsByCategory("Politics", 20);

// Get specific market by slug
const market = await sdk.gamma.getMarketBySlug("trump-2024");
```

### Data API - Positions & Activity

```typescript
// Get wallet positions
const positions = await sdk.data.getPositions("0xYourWalletAddress");
positions.forEach(pos => {
  console.log(`${pos.outcome}: ${pos.size} shares`);
  console.log(`  Entry: $${pos.avgPrice} → Current: $${pos.currentPrice}`);
  console.log(`  PnL: $${pos.pnl.toFixed(2)} (${pos.pnlPercent.toFixed(1)}%)`);
});

// Get portfolio summary
const portfolio = await sdk.data.getPortfolioValue("0xYourWalletAddress");
console.log(`Total Value: $${portfolio.totalValue.toFixed(2)}`);
console.log(`Total PnL: $${portfolio.totalPnl.toFixed(2)}`);
console.log(`Positions: ${portfolio.positionCount}`);

// Get trading activity
const activity = await sdk.data.getActivity("0xYourWalletAddress");

// Get market stats
const stats = await sdk.data.getMarketStats(conditionId);
console.log(`24h Volume: $${stats.volume24h.toLocaleString()}`);
```

### K-Lines - Candlestick Charts

```typescript
// Get hourly candles
const candles = await sdk.klines.getKLines(tokenId, "1h");

candles.forEach(candle => {
  const date = new Date(candle.timestamp).toISOString();
  console.log(`${date}: O:${candle.open} H:${candle.high} L:${candle.low} C:${candle.close}`);
});

// Available intervals: 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w

// Get candles with time range
const dailyCandles = await sdk.klines.getKLines(tokenId, "1d", {
  startTime: Date.now() - 30 * 24 * 60 * 60 * 1000, // 30 days ago
  endTime: Date.now(),
  limit: 30
});

// Get candles for both YES and NO tokens
const { yes, no } = await sdk.klines.getDualKLines(yesTokenId, noTokenId, "1h");
```

### Technical Indicators

```typescript
const candles = await sdk.klines.getKLines(tokenId, "1h", { limit: 100 });

// Simple Moving Average (20-period)
const sma20 = sdk.klines.calculateSMA(candles, 20);

// Exponential Moving Average (12-period)
const ema12 = sdk.klines.calculateEMA(candles, 12);

// Relative Strength Index (14-period)
const rsi = sdk.klines.calculateRSI(candles, 14);
console.log(`Current RSI: ${rsi[rsi.length - 1].toFixed(2)}`);
if (rsi[rsi.length - 1] < 30) console.log("Oversold!");
if (rsi[rsi.length - 1] > 70) console.log("Overbought!");

// Bollinger Bands (20-period, 2 std dev)
const bands = sdk.klines.calculateBollingerBands(candles, 20, 2);
const latest = bands[bands.length - 1];
console.log(`Upper: ${latest.upper.toFixed(3)}`);
console.log(`Middle: ${latest.middle.toFixed(3)}`);
console.log(`Lower: ${latest.lower.toFixed(3)}`);
```

---

## 🧠 Smart Money & Analytics (NEW in v0.8.0!)

Track top traders, detect arbitrage opportunities, and get automated market signals.

### Smart Money Analytics

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// Get top 50 traders by performance
const topTraders = await sdk.analytics.smartMoney.getTopTraders(50);
topTraders.forEach(trader => {
  console.log(`${trader.address.slice(0, 10)}...`);
  console.log(`  PnL: $${trader.pnl.toFixed(2)}`);
  console.log(`  Win Rate: ${(trader.winRate * 100).toFixed(1)}%`);
  console.log(`  Smart Score: ${trader.smartMoneyScore}`);
  console.log(`  Is Smart Money: ${trader.isSmartMoney ? '✅' : '❌'}`);
});

// Check if an address is "smart money"
const isSmartMoney = await sdk.analytics.smartMoney.isSmartMoney("0x...");

// Get smart money activity for a specific market
const activity = await sdk.analytics.smartMoney.getSmartMoneyActivity(conditionId);
console.log(`Buy/Sell Ratio: ${activity.buyRatio.toFixed(2)}`);
console.log(`Sentiment: ${activity.sentiment.toFixed(2)}`); // -1 to 1

// Detect smart money signals across markets
const signals = await sdk.analytics.smartMoney.detectSignals(60); // min 60% strength
signals.forEach(signal => {
  console.log(`${signal.type}: ${signal.description}`);
});
```

### Arbitrage Detection

```typescript
// Detect arbitrage for a specific market
const arb = await sdk.analytics.arbitrage.detectArbitrage(conditionId);

if (arb) {
  console.log(`Type: ${arb.type}`); // LONG or SHORT
  console.log(`YES: $${arb.yesPrice.toFixed(3)}`);
  console.log(`NO: $${arb.noPrice.toFixed(3)}`);
  console.log(`Combined: $${arb.combinedPrice.toFixed(3)}`);
  console.log(`Profit: ${(arb.profitPercent * 100).toFixed(2)}%`);
  console.log(`Profitable After Fees: ${arb.isProfitable}`);
  console.log(`Action: ${arb.action}`);
}

// Scan all markets for arbitrage opportunities
const result = await sdk.analytics.arbitrage.scanMarkets({
  minProfit: 0.005,  // 0.5% minimum profit
  maxMarkets: 100,
  profitableOnly: true,
});

console.log(`Scanned: ${result.totalScanned} markets`);
console.log(`Found: ${result.opportunitiesFound} opportunities`);
console.log(`Best: ${result.bestOpportunity?.profitPercent.toFixed(2)}%`);

// Calculate profit for a specific trade size
const profit = sdk.analytics.arbitrage.calculateProfit(arb, 1000); // $1000
console.log(`Gross Profit: $${profit.grossProfit.toFixed(2)}`);
console.log(`Fees: $${profit.fees.toFixed(2)}`);
console.log(`Net Profit: $${profit.netProfit.toFixed(2)}`);
```

### Market Signals

```typescript
// Detect signals for a specific market
const signals = await sdk.analytics.signals.detectSignals(conditionId);

signals.forEach(signal => {
  console.log(`${signal.type} (${signal.confidence}% confidence)`);
  console.log(`  ${signal.description}`);
  console.log(`  Action: ${signal.action}`);
});

// Scan all markets for signals
const result = await sdk.analytics.signals.scanForSignals({
  minConfidence: 60,
  maxMarkets: 50,
});

console.log(`Total signals: ${result.totalSignals}`);
console.log(`Top signals:`, result.topSignals);

// Get buy/sell signals specifically
const buySignals = await sdk.analytics.signals.getBuySignals(10);
const sellSignals = await sdk.analytics.signals.getSellSignals(10);

// Subscribe to real-time signals
const unsubscribe = sdk.analytics.signals.subscribe((signal) => {
  console.log(`[${signal.type}] ${signal.description}`);
});

// Start automatic signal scanning
sdk.analytics.signals.startAutoScan(60000); // Every minute

// Stop scanning
sdk.analytics.signals.stopAutoScan();

// Unsubscribe
unsubscribe();
```

**Signal Types:**
- `VOLUME_SPIKE` - Unusual volume increase
- `PRICE_MOMENTUM_UP/DOWN` - Strong price movement
- `WHALE_BUY/SELL` - Large single trades (>$10k)
- `SMART_MONEY_BUY/SELL` - Smart money activity
- `BREAKOUT/BREAKDOWN` - Price breaking key levels
- `UNUSUAL_ACTIVITY` - High frequency + volume
- `LIQUIDITY_SURGE` - High liquidity available

### Market Summary

```typescript
// Get overall market conditions
const summary = await sdk.analytics.getMarketSummary();

console.log(`Smart Money Traders: ${summary.topTraders}`);
console.log(`Arbitrage Opportunities: ${summary.arbitrageOpportunities}`);
console.log(`Active Signals: ${summary.activeSignals}`);
console.log(`Market Sentiment: ${summary.marketSentiment}`); // BULLISH, BEARISH, NEUTRAL
```

---

## 🎯 Arbitrage Executor (NEW in v0.8.1!)

Auto-execute arbitrage opportunities with real-time monitoring.

### Basic Usage

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK({
  trading: {
    chainId: 137,
    backend: { privateKey: process.env.PRIVATE_KEY },
  },
});

// Create executor with auto-execute enabled
const executor = sdk.arbitrage.createExecutor({
  profitThreshold: 0.005,  // 0.5% minimum profit
  autoExecute: true,       // Auto-execute opportunities
  maxTradeSize: 1000,      // Maximum $1000 per trade
  minTradeSize: 10,        // Minimum $10 per trade
  maxSlippage: 0.02,       // 2% max slippage
  orderType: 'FOK',        // Fill-or-Kill orders
  scanInterval: 5000,      // Scan every 5 seconds
  dryRun: true,            // Start in dry-run mode
});

// Listen for opportunities
executor.on('opportunity', (opp) => {
  console.log(`${opp.type} ARB: ${(opp.profitPercent * 100).toFixed(2)}% profit`);
  console.log(`Action: ${opp.action}`);
});

// Listen for executions
executor.on('execution', (result) => {
  console.log(`Execution ${result.status}`);
  console.log(`Net Profit: $${result.netProfit.toFixed(2)}`);
});

// Start monitoring a specific market
await executor.start('condition-id-here');

// Or find and start the best opportunity
const best = await executor.findAndStart(0.005);
if (best) {
  console.log(`Monitoring: ${best.market.question}`);
}

// Get statistics
const stats = executor.getStats();
console.log(`Opportunities: ${stats.opportunitiesDetected}`);
console.log(`Executions: ${stats.executionsSuccessful}/${stats.executionsAttempted}`);
console.log(`Total Profit: $${stats.totalNetProfit.toFixed(2)}`);

// Stop monitoring
executor.stop();
```

### Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `profitThreshold` | number | 0.005 | Minimum profit (0.005 = 0.5%) |
| `autoExecute` | boolean | false | Auto-execute opportunities |
| `minTradeSize` | number | 10 | Minimum trade size (USD) |
| `maxTradeSize` | number | 1000 | Maximum trade size (USD) |
| `maxSlippage` | number | 0.02 | Maximum slippage (0.02 = 2%) |
| `orderType` | 'FOK' \| 'FAK' | 'FOK' | Order type for execution |
| `scanInterval` | number | 5000 | Scan interval (ms) |
| `dryRun` | boolean | true | Dry run mode (no real trades) |

---

## 📋 Copy Trading (NEW in v0.8.1!)

Automatically copy trades from smart money wallets in real-time.

### Basic Usage

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK({
  trading: {
    chainId: 137,
    backend: { privateKey: process.env.PRIVATE_KEY },
  },
});

// Create copy trading service
const copier = sdk.copyTrading.create({
  topN: 50,                    // Follow top 50 traders
  sizeScale: 0.1,              // Copy 10% of their trade size
  maxSizePerTrade: 100,        // Max $100 per copy trade
  minSizePerTrade: 5,          // Min $5 per copy trade
  minLeaderTradeSize: 10,      // Only copy trades > $10
  minSmartScore: 50,           // Only follow high-score traders
  maxSlippage: 0.03,           // 3% max slippage
  orderType: 'FOK',            // Fill-or-Kill orders
  dryRun: true,                // Start in dry-run mode
});

// Listen for copied trades
copier.on('trade', (event, result) => {
  console.log(`Leader: ${event.leaderTrade.address.slice(0, 10)}...`);
  console.log(`Side: ${event.leaderTrade.side}`);
  console.log(`Copy Size: $${event.copySize.toFixed(2)}`);
  console.log(`Result: ${result.success ? '✅' : '❌'}`);
});

// Listen for leader trades (before copy decision)
copier.on('leader_trade', (trade) => {
  console.log(`Smart money trade: ${trade.side} $${trade.value.toFixed(2)}`);
});

// Start copy trading
await copier.start();

// Get statistics
const stats = copier.getStats();
console.log(`Leaders followed: ${stats.leadersFollowed}`);
console.log(`Trades copied: ${stats.tradesExecuted}`);
console.log(`Success rate: ${(stats.successRate * 100).toFixed(1)}%`);
console.log(`Volume copied: $${stats.totalVolumeCopied.toFixed(2)}`);

// Add/remove addresses to follow
copier.addAddress('0x...');
copier.removeAddress('0x...');

// Refresh followed addresses
await copier.refreshFollowedAddresses();

// Stop copy trading
copier.stop();
```

### Configuration Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topN` | number | 50 | Follow top N traders |
| `targetAddresses` | string[] | [] | Specific addresses to follow |
| `sizeScale` | number | 0.1 | Scale factor (0.1 = 10%) |
| `maxSizePerTrade` | number | 100 | Maximum copy size (USD) |
| `minSizePerTrade` | number | 5 | Minimum copy size (USD) |
| `minLeaderTradeSize` | number | 10 | Min leader trade to copy |
| `minSmartScore` | number | 50 | Minimum smart money score |
| `maxSlippage` | number | 0.03 | Maximum slippage (3%) |
| `orderType` | string | 'FOK' | Order type |
| `sideFilter` | 'BUY' \| 'SELL' | undefined | Only copy BUY or SELL |
| `dryRun` | boolean | true | Dry run mode |

---

## ⛓️ On-Chain Service (NEW in v0.8.1!)

Query balances, check approvals, and prepare for trading.

### Balance Queries

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// Initialize the on-chain service
await sdk.onchain.initialize();

// Get all balances
const balances = await sdk.onchain.getBalances('0xYourAddress');
console.log(`MATIC: ${balances.matic}`);
console.log(`USDC.e: ${balances.usdcE}`);
console.log(`USDC: ${balances.usdc}`);

// Get specific token balance
const token = await sdk.onchain.getTokenBalance(
  '0xYourAddress',
  '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174' // USDC.e
);
console.log(`${token.symbol}: ${token.formatted}`);

// Get CTF token balances
const ctfBalances = await sdk.onchain.getCTFBalances(
  '0xYourAddress',
  ['tokenId1', 'tokenId2']
);
ctfBalances.forEach(b => {
  console.log(`Token ${b.tokenId}: ${b.balance}`);
});
```

### Approval Management

```typescript
// Check if ready for trading
const status = await sdk.onchain.checkReadyForTrading('1000'); // $1000
console.log(`Ready: ${status.ready}`);
console.log(`Has balance: ${status.hasSufficientBalance}`);
console.log(`USDC.e: ${status.usdcBalance}`);

if (!status.ready) {
  console.log('Issues:', status.issues);
  
  // Approve all required tokens
  const result = await sdk.onchain.approveAll();
  console.log(`Approvals: ${result.approvals.length}`);
  console.log(`Success: ${result.success}`);
}

// Check specific approval
const approval = await sdk.onchain.checkApproval(
  '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', // USDC.e
  '0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E', // CTF Exchange
  '1000'
);
console.log(`Approved: ${approval.isApproved}`);
console.log(`Allowance: ${approval.allowance}`);

// Approve specific token
const tx = await sdk.onchain.approve(
  '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
  '0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E'
);
console.log(`TX Hash: ${tx.hash}`);
```

### Polygon Contract Addresses

```typescript
import { POLYGON_CONTRACTS } from "@polybased/sdk";

console.log(POLYGON_CONTRACTS.USDC_E);              // Bridged USDC
console.log(POLYGON_CONTRACTS.USDC);                // Native USDC
console.log(POLYGON_CONTRACTS.WMATIC);              // Wrapped MATIC
console.log(POLYGON_CONTRACTS.CTF_EXCHANGE);        // Polymarket CTF Exchange
console.log(POLYGON_CONTRACTS.NEG_RISK_CTF_EXCHANGE); // Neg Risk Exchange
console.log(POLYGON_CONTRACTS.NEG_RISK_ADAPTER);    // Neg Risk Adapter (v0.9.0)
console.log(POLYGON_CONTRACTS.CONDITIONAL_TOKENS);  // CTF Contract
```

---

## 🌉 Bridge API (NEW in v0.9.0!)

Cross-chain deposits from EVM, Solana, and Bitcoin into Polymarket.

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// Get deposit addresses for your wallet
const addresses = await sdk.bridge.getDepositAddresses("0xYourWalletAddress");
console.log(`EVM deposit:    ${addresses.evm?.depositAddress}`);
console.log(`Solana deposit: ${addresses.svm?.depositAddress}`);
console.log(`Bitcoin deposit: ${addresses.btc?.depositAddress}`);

// Check supported assets
const assets = await sdk.bridge.getSupportedAssets();
assets.forEach(a => console.log(`${a.chain}: ${a.symbol}`));

// Check deposit status
const status = await sdk.bridge.getDepositStatus("0xDepositAddress");
console.log(`Status: ${status.status}`);
```

---

## 📊 Subgraph Client (NEW in v0.9.0!)

Query on-chain data from Polymarket's 5 Goldsky subgraphs.

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// Get user positions across all markets
const positions = await sdk.subgraph.queryPositions("0xYourWalletAddress");
positions.forEach(p => {
  console.log(`Token: ${p.tokenId} | Balance: ${p.balance}`);
});

// Get open interest for a market
const oi = await sdk.subgraph.queryOpenInterest("0xConditionId...");
console.log(`Condition: ${oi.conditionId} | OI: ${oi.openInterest}`);

// Get user PnL
const pnl = await sdk.subgraph.queryPnL("0xYourWalletAddress");
pnl.forEach(p => {
  console.log(`Market ${p.conditionId}: PnL = ${p.realized}`);
});

// Custom GraphQL query
const result = await sdk.subgraph.rawQuery("positions", `{
  userBalances(where: { user: "0x..." }, first: 10) {
    id
    balance
    tokenId
  }
}`);
```

---

## ⛓️ CTF Operations (NEW in v0.9.0!)

Split, merge, and redeem conditional tokens on-chain.

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK({
  trading: {
    chainId: 137,
    backend: { privateKey: process.env.PRIVATE_KEY! },
  },
});

await sdk.onchain.initialize();

// Split $100 USDC into 100 YES + 100 NO tokens
const splitResult = await sdk.onchain.split("0xConditionId...", "100");
console.log(`Split TX: ${splitResult.transactionHash}`);

// Merge 50 YES + 50 NO tokens back into $50 USDC
const mergeResult = await sdk.onchain.merge("0xConditionId...", "50");
console.log(`Merge TX: ${mergeResult.transactionHash}`);

// Redeem winning tokens after market resolution
const redeemResult = await sdk.onchain.redeem("0xConditionId...");
console.log(`Redeem TX: ${redeemResult.transactionHash}`);

// Redeem neg-risk positions
const negResult = await sdk.onchain.redeemNegRisk("0xConditionId...", ["100", "0"]);
console.log(`NegRisk TX: ${negResult.transactionHash}`);

// Calculate condition/collection/position IDs
const conditionId = await sdk.onchain.getConditionId(oracle, questionId, 2);
const collectionId = await sdk.onchain.getCollectionId(conditionId, 1); // YES
const positionId = await sdk.onchain.getPositionId(collectionId);
```

---

## 🏷️ Enhanced Gamma & Data API (v0.9.0!)

### Gamma: Tags, Comments, Search, Series, Profiles, Sports

```typescript
const sdk = new PolybasedSDK();

// Tags
const tags = await sdk.gamma.getTags();
const related = await sdk.gamma.getRelatedTags("politics");

// Comments
const comments = await sdk.gamma.getComments("event", "eventId");

// Official public search
const results = await sdk.gamma.publicSearch("bitcoin");

// Series (recurring events)
const series = await sdk.gamma.getSeries();

// User profiles
const profile = await sdk.gamma.getProfile("0xWalletAddress");

// Sports
const sports = await sdk.gamma.getSports();
const teams = await sdk.gamma.getSportTeams("NFL");
```

### Data: Closed Positions, Holders, Open Interest, Builder

```typescript
// Closed/resolved positions
const closed = await sdk.data.getClosedPositions("0xYourAddress");

// Token holders
const holders = await sdk.data.getHolders("0xConditionId");

// Open interest
const oi = await sdk.data.getOpenInterest("0xConditionId");
console.log(`OI: $${oi.openInterest}`);

// Volume by event
const vol = await sdk.data.getVolume("eventId");

// Check if user ever traded
const traded = await sdk.data.getTradedStatus("0xAddress");

// Builder leaderboard
const builders = await sdk.data.getBuilderLeaderboard({ period: "7d" });
```

---

## 🔬 AI-Powered Research (NEW in v0.6.0!)

Get AI-powered insights and analysis for any Polymarket question using the **[FactsAI Deep Research API](https://factsai.org)**.

### What is FactsAI?

**FactsAI** (formerly Polyfactual) provides AI-powered deep research capabilities that analyze information from across the web and return comprehensive answers with detailed citations. It's perfect for:

- 📊 Market analysis and trend research
- 🔍 Due diligence on prediction markets
- 🤖 AI trading bots that need context
- 📈 Sentiment analysis and data gathering
- 💡 Understanding complex market factors

**Official Website:** [https://factsai.org](https://factsai.org)  
**API Documentation:** [https://factsai.org/docs](https://factsai.org/docs)

---

### Quick Start

```typescript
import { ResearchClient } from '@polybased/sdk';

// Initialize the research client
const client = new ResearchClient({
  apiKey: process.env.RESEARCH_API_KEY, // Get from factsai.org
  timeout: 300000,  // 5 minutes (research takes time!)
  retries: 3,       // Auto-retry on failures
  debug: true       // Optional: see detailed logs
});

// Ask a research question
const result = await client.query(
  'What factors are influencing Bitcoin prediction markets in 2024?'
);

if (result?.success) {
  console.log('Answer:', result.data.answer);
  console.log('Cost: $', result.data.costDollars.total); // $0.012
  console.log('Citations:', result.data.citations?.length);
  
  // Access detailed citations
  result.data.citations?.forEach(citation => {
    console.log(`- ${citation.title}`);
    console.log(`  URL: ${citation.url}`);
    console.log(`  Author: ${citation.author}`);
    console.log(`  Published: ${citation.publishedDate}`);
  });
}
```

---

### Complete Example: AI-Powered Trading Bot

Combine market data with AI research for smarter trading decisions:

```typescript
import { PolybasedSDK, ResearchClient } from '@polybased/sdk';

// Initialize SDK with research
const sdk = new PolybasedSDK({
  trading: {
    chainId: 137,
    backend: { privateKey: process.env.PRIVATE_KEY }
  }
});

const research = new ResearchClient({
  apiKey: process.env.RESEARCH_API_KEY
});

// AI-powered trading strategy
async function aiTradingBot() {
  // 1. Find markets
  const markets = await sdk.searchMarkets('Bitcoin');
  const market = markets[0];
  
  // 2. Get AI analysis
  const analysis = await research.query(
    `Should I buy Bitcoin prediction market shares? 
     Current price: ${market.prices?.YES}. 
     Analyze recent trends and sentiment.`
  );
  
  if (analysis?.success) {
    console.log('AI Analysis:', analysis.data.answer);
    console.log('Research Cost: $', analysis.data.costDollars.total);
    
    // 3. Make decision based on AI
    const shouldBuy = analysis.data.answer.toLowerCase().includes('positive');
    
    if (shouldBuy) {
      const trading = sdk.trading.init();
      const order = await trading.placeOrder({
        tokenId: market.raw.tokens[0].token_id,
        side: 'BUY',
        price: 0.65,
        size: 10
      });
      
      console.log('✅ Order placed based on AI analysis!');
      console.log('Order ID:', order.orderId);
    }
  }
}

aiTradingBot();
```

---

### Features

**🏗️ Enterprise-Grade Reliability:**
- ✅ **Circuit breaker pattern** - Prevents cascading failures
- ✅ **Automatic retries** - Exponential backoff (1s → 2s → 4s → 8s)
- ✅ **Graceful degradation** - Returns null on failure, never crashes
- ✅ **Health monitoring** - Check service status anytime

**💰 Cost Tracking:**
- ✅ **Transparent pricing** - $0.012 per request (1 credit)
- ✅ **Cost in response** - Every result includes `costDollars.total`
- ✅ **Bulk packages available** - $12 (1K), $60 (5K), $120 (10K credits)

**📚 Detailed Citations:**
- ✅ **Source URLs** - Direct links to information sources
- ✅ **Author & publish dates** - Full citation metadata
- ✅ **Article text** - Optional full text extraction
- ✅ **Images & favicons** - Visual elements for UI display

**🔒 Type Safety:**
- ✅ **Full TypeScript types** - Complete autocomplete support
- ✅ **Error handling** - Proper error types (400, 401, 402, 429, 500)
- ✅ **Input validation** - Query length limits (max 1,000 chars)

---

### Configuration Options

```typescript
interface ResearchClientConfig {
  apiKey: string;           // Required: Your FactsAI API key
  baseUrl?: string;         // Optional: API base URL (default: factsai.org)
  timeout?: number;         // Optional: Request timeout (default: 300000ms = 5min)
  retries?: number;         // Optional: Number of retries (default: 3)
  debug?: boolean;          // Optional: Enable debug logs (default: false)
  onError?: (error: Error) => void;      // Optional: Error callback
  onWarning?: (message: string) => void; // Optional: Warning callback
}
```

---

### API Methods

#### `query(question: string, text?: boolean): Promise<ResearchResponse | null>`

Query the research API with a question.

**Parameters:**
- `question` (string): Research question (max 1,000 characters)
- `text` (boolean, optional): Include full text in citations (default: true)

**Returns:** `ResearchResponse` or `null` on failure

```typescript
const result = await client.query('What factors affect Bitcoin markets?');
```

#### `healthCheck(): Promise<boolean>`

Check if the research service is available.

**Returns:** `true` if healthy, `false` otherwise

```typescript
const isHealthy = await client.healthCheck();
console.log('Service healthy:', isHealthy);
```

#### `getStatus(): ResearchHealthStatus`

Get current service status without making an API call.

**Returns:** Object with `healthy` and `timestamp` fields

```typescript
const status = client.getStatus();
console.log('Status:', status.healthy);
```

#### `resetCircuitBreaker(): void`

Manually reset the circuit breaker if you've fixed issues.

```typescript
client.resetCircuitBreaker();
```

---

### Response Format

```typescript
interface ResearchResponse {
  success: boolean;
  data: {
    answer: string;                    // AI-generated answer
    citations?: ResearchCitation[];    // Source citations
    costDollars: {
      total: number;                   // Cost in USD ($0.012)
    };
  };
  timestamp: string;                   // Response timestamp
}

interface ResearchCitation {
  id: string;                          // Citation ID
  url: string;                         // Source URL
  title: string;                       // Article title
  author?: string;                     // Article author
  publishedDate?: string;              // Publish date
  text?: string;                       // Full article text (if requested)
  image?: string;                      // Featured image URL
  favicon?: string;                    // Site favicon URL
}
```

---

### Error Handling

The client handles errors gracefully and returns `null` on failure:

```typescript
const result = await client.query('What is Bitcoin?');

if (result?.success) {
  // Success! Use the data
  console.log('Answer:', result.data.answer);
  console.log('Cost: $', result.data.costDollars.total);
} else {
  // Failed - check logs or service status
  console.log('Query failed, check your API key and service status');
  
  const status = client.getStatus();
  console.log('Service healthy:', status.healthy);
}
```

**Common Error Codes:**
- **400** - Bad Request (invalid query format)
- **401** - Unauthorized (invalid API key)
- **402** - Payment Required (insufficient credits - [buy more](https://factsai.org))
- **429** - Too Many Requests (rate limit: 100/min - slow down)
- **500** - Server Error (temporary issue - retries automatically)

---

### Rate Limits & Pricing

**Rate Limits:**
- 100 requests per minute per API key
- Query length: Max 1,000 characters
- Timeout: 5 minutes default (configurable)

**Pricing:**
- **$0.012 per request** (1 credit)
- **Bulk packages:**
  - $12 → 1,000 credits
  - $60 → 5,000 credits
  - $120 → 10,000 credits

**Get API Key:** [https://factsai.org](https://factsai.org)

---

### Best Practices

**✅ DO:**
- Store API key in environment variables (`process.env.RESEARCH_API_KEY`)
- Enable debug mode during development (`debug: true`)
- Check `result?.success` before using data
- Track costs with `result.data.costDollars.total`
- Use circuit breaker for automatic failure handling
- Validate query length (max 1,000 chars)

**❌ DON'T:**
- Hardcode API keys in your code
- Make requests faster than 100/min (rate limit)
- Ignore null responses (means service unavailable)
- Set timeout too low (research takes time!)
- Bypass input validation

---

### Documentation

**Comprehensive guides available:**
- 📖 **[API Examples](./API_EXAMPLES.md)** - Complete code examples (cURL, Node.js, Python, TypeScript)
- 🔑 **[API Key Setup](./API_KEY_SETUP.md)** - How to get your API key and purchase credits
- 🌐 **[Official Docs](https://factsai.org/docs)** - FactsAI API documentation

---

## 💰 Trading Quickstart

The SDK supports all Polymarket order types and trading features.

### Order Types (NEW in v0.7.0!)

| Type | Description | Use Case |
|------|-------------|----------|
| **GTC** | Good Till Cancelled | Default, stays until filled/cancelled |
| **GTD** | Good Till Date | Expires at specified timestamp |
| **FOK** | Fill or Kill | Must fill entirely or cancel immediately |
| **FAK** | Fill and Kill | Partial fill ok, rest cancelled |

### Backend Trading (Bots & Servers)

```typescript
import { PolybasedSDK } from "@polybased/sdk";

// Initialize SDK with trading configuration
const sdk = new PolybasedSDK({
  trading: {
    chainId: 137, // Polygon mainnet
    backend: {
      privateKey: process.env.PRIVATE_KEY,
    },
  },
});

const trading = sdk.trading.init();

// ===== LIMIT ORDERS =====

// GTC order (default) - stays until filled or cancelled
const gtcOrder = await trading.placeOrder({
  tokenId: "0x123...",
  side: "BUY",
  price: 0.65,
  size: 10,
  orderType: "GTC" // optional, GTC is default
});

// GTD order - expires in 1 hour
const gtdOrder = await trading.placeOrder({
  tokenId: "0x123...",
  side: "BUY",
  price: 0.65,
  size: 10,
  orderType: "GTD",
  expiration: Math.floor(Date.now() / 1000) + 3600
});

// FOK order - fill all or nothing
const fokOrder = await trading.placeOrder({
  tokenId: "0x123...",
  side: "BUY",
  price: 0.65,
  size: 10,
  orderType: "FOK"
});

// ===== MARKET ORDERS (NEW!) =====

// Buy $100 worth immediately (FOK)
const marketBuy = await trading.placeMarketOrder({
  tokenId: "0x123...",
  side: "BUY",
  amount: 100, // $100 USD
  orderType: "FOK"
});

// Sell 50 shares immediately (FAK - partial fill ok)
const marketSell = await trading.placeMarketOrder({
  tokenId: "0x123...",
  side: "SELL",
  amount: 50, // 50 shares
  orderType: "FAK"
});

// ===== ORDER MANAGEMENT =====

const openOrders = await trading.getOpenOrders();
await trading.cancelOrder(order.orderId);
await trading.cancelOrders(["id1", "id2"]);
await trading.cancelAllOrders(); // ⚠️ Cancels ALL orders!
await trading.cancelMarketOrders("condition-id"); // Cancel for specific market

// ===== MARKET INFO =====

const tickSize = await trading.getTickSize(tokenId); // "0.01" or "0.001"
const negRisk = await trading.getNegRisk(tokenId);   // true/false
const feeRate = await trading.getFeeRate(tokenId);   // basis points
const spread = await trading.getSpread(tokenId);     // bid-ask spread

// ===== BATCH OPERATIONS =====

const books = await trading.getOrderbooks([
  { token_id: "0x123..." },
  { token_id: "0x456..." }
]);
const midpoints = await trading.getMidpoints([...]);
const spreads = await trading.getSpreads([...]);

// ===== REWARDS & EARNINGS =====

const isScoring = await trading.isOrderScoring(orderId);
const earnings = await trading.getEarnings("2025-01-14");
const totalEarnings = await trading.getTotalEarnings("2025-01-14");
const currentRewards = await trading.getCurrentRewards();
const marketRewards = await trading.getMarketRewards(conditionId);

// ===== NOTIFICATIONS =====

const notifications = await trading.getNotifications();
await trading.dropNotifications(["notif-id-1", "notif-id-2"]);

// ===== BALANCE & ALLOWANCE =====

const balance = await trading.getBalanceAllowance();
await trading.updateBalanceAllowance(); // Set max allowance

// ===== TRADE HISTORY =====

const trades = await trading.getTrades({ market: conditionId });
```

### Frontend Trading (Web Apps)

For browser-based applications where users connect their own wallets:

```typescript
import { PolybasedSDK } from "@polybased/sdk";
import { ethers } from "ethers";

// Connect to user's wallet (MetaMask, WalletConnect, etc.)
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send("eth_requestAccounts", []);
const signer = provider.getSigner();

// Initialize SDK with frontend mode
const sdk = new PolybasedSDK({
  trading: {
    chainId: 137,
    frontend: {
      signer: signer,
    },
    // Optional: Signing server for builder attribution
    builder: {
      signingServerUrl: "https://your-signing-server.com",
    },
  },
});

const trading = sdk.trading.init();

// Place order (user will sign the transaction)
const order = await trading.placeOrder({
  tokenId: "0x123...",
  side: "BUY",
  price: 0.65,
  size: 10,
});
```

### ⚠️ Security Notes

**IMPORTANT:**
- 🔒 **Never expose your private key** in frontend code or public repositories
- ✅ Use environment variables for private keys (backend mode)
- ✅ Use frontend mode for web apps (users control their own keys)
- ✅ Always add `.env` to your `.gitignore`

---

## 🔄 Relayer (Gasless Transactions)

**ADVANCED FEATURE** - For production apps that want to pay gas fees for users.

### What is a Relayer?

A relayer is a service that executes transactions and pays the gas fees on your behalf, enabling **gasless trading** experiences.

**When to use:**
- ✅ Building web apps where you sponsor user gas fees
- ✅ Creating better UX (users don't need ETH/MATIC for gas)
- ✅ Meta-transactions and delegated trading

**When NOT to use:**
- ❌ Simple trading bots (use regular trading instead)
- ❌ Personal trading (just pay your own gas)
- ❌ Development/testing (use regular trading)

### Backend Relayer (Automated Systems)

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK({
  relayer: {
    chainId: 137,
    backend: {
      privateKey: process.env.PRIVATE_KEY,
    },
  },
});

const relayer = sdk.relayer.init();

// Execute gasless transaction
const response = await relayer.executeProxyTransactions([
  {
    to: "0x...",      // Contract address
    typeCode: "1",    // Call type (1 = Call)
    data: "0x...",    // Encoded function call
    value: "0"        // ETH value (usually "0")
  }
]);

// Wait for confirmation
const result = await relayer.waitForTransaction(response.transactionId);
console.log("Transaction confirmed:", result.transactionHash);
```

### Frontend Relayer (Web Apps)

```typescript
import { PolybasedSDK } from "@polybased/sdk";
import { ethers } from "ethers";

const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();

const sdk = new PolybasedSDK({
  relayer: {
    chainId: 137,
    frontend: {
      signer: signer,
    },
  },
});

const relayer = sdk.relayer.init();

// User signs gasless transaction (you pay the gas)
const response = await relayer.executeProxyTransactions([...]);
```

### Relayer Methods

- `relayer.getRelayerAddress()` - Get the relayer's address
- `relayer.getNonce()` - Get current nonce for your wallet
- `relayer.executeProxyTransactions(txs)` - Execute gasless transactions
- `relayer.executeSafeTransactions(txs)` - Execute Safe multi-sig transactions
- `relayer.getTransaction(id)` - Get transaction status
- `relayer.getTransactions()` - Get all your transactions
- `relayer.waitForTransaction(id)` - Wait for transaction confirmation
- `relayer.deploySafe()` - Deploy a Gnosis Safe wallet

---

## 📖 API Reference

### `PolybasedSDK`

The main SDK class for interacting with Polymarket.

#### Constructor

```typescript
const sdk = new PolybasedSDK(config?: PolybasedConfig);
```

**Configuration Options:**

```typescript
interface PolybasedConfig {
  metaBaseUrl?: string;  // API base URL (default: "https://clob.polymarket.com")
  wsBaseUrl?: string;    // WebSocket base URL (default: "wss://clob.polymarket.com/ws")
  debug?: boolean;       // Enable debug logging (default: false)
  trading?: TradingConfig; // Trading configuration (optional)
}

interface TradingConfig {
  host?: string;         // CLOB host (default: "https://clob.polymarket.com")
  chainId?: number;      // Chain ID (default: 137 for Polygon)
  
  // Backend mode (for bots/servers)
  backend?: {
    privateKey: string;  // Your wallet private key
  };
  
  // Frontend mode (for web apps)
  frontend?: {
    signer: any;         // Ethers.js signer from user's wallet
  };
  
  // Builder credentials (optional)
  builder?: {
    key?: string;        // Builder API key
    secret?: string;     // Builder API secret
    passphrase?: string; // Builder API passphrase
    signingServerUrl?: string; // Signing server URL (frontend mode)
  };
}
```

#### Methods

##### `getMarkets(): Promise<Market[]>`

Fetch all active markets from Polymarket.

**Returns:** Array of `Market` objects.

```typescript
const markets = await sdk.getMarkets();
```

##### `getMarket(marketId: string): Promise<Market>`

Fetch a single market by its ID.

**Parameters:**
- `marketId` (string): The unique identifier for the market

**Returns:** A `Market` object.

```typescript
const market = await sdk.getMarket("some-market-id");
```

##### `searchMarkets(query: string): Promise<Market[]>` ✨ NEW

Search for markets by keyword. Searches market questions and slugs.

**Parameters:**
- `query` (string): Search keyword (case-insensitive)

**Returns:** Array of matching `Market` objects.

```typescript
// Find Bitcoin-related markets
const btcMarkets = await sdk.searchMarkets("bitcoin");
console.log(`Found ${btcMarkets.length} markets`);
```

##### `findMarket(slugOrQuery: string): Promise<Market | null>` ✨ NEW

Find a single market by keyword or slug. Returns first match.

**Parameters:**
- `slugOrQuery` (string): Market slug or search keyword

**Returns:** A `Market` object, or `null` if not found.

```typescript
// Find a specific market
const market = await sdk.findMarket("trump");
if (market) {
  console.log('Found:', market.question);
}
```

##### `onOrderbook(marketId, callback, options?): () => void`

Subscribe to real-time orderbook updates for a specific market.

**Parameters:**
- `marketId` (string): The market ID to subscribe to
- `callback` (function): Called with each orderbook update
- `options` (optional): Event handlers for WebSocket lifecycle

**Returns:** Unsubscribe function

```typescript
const unsubscribe = sdk.onOrderbook(
  "market-id",
  (update) => console.log(update),
  {
    onOpen: () => console.log("Connected"),
    onClose: () => console.log("Disconnected"),
    onError: (err) => console.error(err)
  }
);
```

### Trading API

Access trading functionality via `sdk.trading.init()`.

#### `sdk.trading.init(): TradingClient`

Initialize and return a trading client.

**Returns:** TradingClient instance

**Throws:** Error if trading configuration was not provided

```typescript
const trading = sdk.trading.init();
```

### `TradingClient`

Trading client for placing and managing orders.

#### `placeOrder(params): Promise<{ orderId: string; status: string }>`

Place a limit order on Polymarket.

**Parameters:**

```typescript
interface PlaceOrderParams {
  tokenId: string;     // Token ID to trade
  side: "BUY" | "SELL"; // Order side
  price: number;       // Price (0.01 to 0.99)
  size: number;        // Order size/quantity
  expiration?: number; // Optional expiration timestamp
  nonce?: number;      // Optional nonce
}
```

**Returns:** Promise with order ID and status

**Example:**

```typescript
const order = await trading.placeOrder({
  tokenId: "0x123...",
  side: "BUY",
  price: 0.65,
  size: 10
});
```

#### `cancelOrder(orderId): Promise<void>`

Cancel an existing order.

**Parameters:**
- `orderId` (string): The order ID to cancel

**Example:**

```typescript
await trading.cancelOrder("0xabc123...");
```

#### `cancelOrders(orderIds): Promise<void>`

Cancel multiple orders at once.

**Parameters:**
- `orderIds` (string[]): Array of order IDs to cancel

**Example:**

```typescript
await trading.cancelOrders(["0xabc...", "0xdef..."]);
```

#### `getOpenOrders(address?): Promise<Order[]>`

Get all open orders for a wallet address.

**Parameters:**
- `address` (string, optional): Wallet address (uses your wallet if not provided)

**Returns:** Promise resolving to array of Order objects

**Example:**

```typescript
const orders = await trading.getOpenOrders();
orders.forEach(order => {
  console.log(`${order.side} ${order.size} @ $${order.price}`);
});
```

#### `getOrder(orderId): Promise<Order | null>`

Get details of a specific order.

**Parameters:**
- `orderId` (string): The order ID to look up

**Returns:** Promise resolving to Order object or null

**Example:**

```typescript
const order = await trading.getOrder("0xabc123...");
console.log(`Order status: ${order?.status}`);
```

#### `getWalletAddress(): string | undefined`

Get the wallet address being used for trading.

**Returns:** The wallet address or undefined

#### `getRawClient(): ClobClient`

Get the underlying CLOB client for advanced usage.

**Returns:** The underlying ClobClient instance

---

## 🔧 Type Definitions

### `Market`

```typescript
interface Market {
  id: string;                              // Unique market identifier
  question: string;                        // Market question/title
  outcomes: string[];                      // Possible outcomes (e.g., ["YES", "NO"])
  status?: "open" | "closed" | "resolved"; // Market status
  prices?: Record<string, number>;         // Current prices by outcome
  volumeUsd?: number;                      // Total volume in USD
  liquidityUsd?: number;                   // Available liquidity in USD
  raw?: any;                               // Raw API response
}
```

### `OrderbookUpdate`

```typescript
type OrderbookUpdate =
  | { type: "snapshot"; data: OrderbookSnapshot }
  | { type: "delta"; data: Partial<OrderbookSnapshot> };

interface OrderbookSnapshot {
  marketId: string;
  bids: OrderbookLevel[];
  asks: OrderbookLevel[];
  ts: number;
}

interface OrderbookLevel {
  price: number;
  size: number;
}
```

### `Order`

```typescript
interface Order {
  orderId: string;      // Unique order ID
  tokenId: string;      // Token ID being traded
  side: "BUY" | "SELL"; // Order side
  price: number;        // Order price
  originalSize: number; // Original order size
  size: number;         // Remaining unfilled size
  status: string;       // Order status
  timestamp: number;    // Order creation timestamp
  raw?: any;           // Raw order data from API
}
```

---

## ❓ FAQ

### Is this SDK only for data/market streaming?

**No!** The SDK now provides three main features:
1. **Data fetching** - Get market information
2. **Real-time streaming** - Subscribe to live orderbook updates
3. **Trading** - Place, cancel, and manage orders (NEW!)

You can use just the data features without setting up trading, or use the complete trading functionality.

### Why do I need builder config? How is it used?

**Builder configuration is OPTIONAL** but recommended for serious traders.

**What it does:**
- Attributes your orders to your builder account
- Helps with analytics and tracking your order flow
- May provide fee rebates or benefits from Polymarket
- Builds your reputation as a market maker

**How to get it:**
Contact Polymarket to request builder credentials.

**How it's used:**
- **Backend mode**: Pass your builder credentials (key, secret, passphrase)
- **Frontend mode**: Use a signing server URL that you control

### Backend vs Frontend mode - which should I use?

**Use Backend Mode if:**
- 🤖 Building a trading bot or automated strategy
- 🖥️ Running a server-side application
- 🔐 You control the private key

**Use Frontend Mode if:**
- 🌐 Building a web application
- 👤 Users connect their own wallets (MetaMask, etc.)
- 🔒 Users control their own keys

**Security:**
- Backend: Private key is on your server (ensure server security!)
- Frontend: Users control their own keys (more secure for users)

### How do I get a token ID?

Token IDs are found in the market data:

```typescript
const sdk = new PolybasedSDK();
const market = await sdk.getMarket("market-id");

// Inspect the raw data to find token IDs
console.log(market.raw);

// Markets typically have 2 tokens: one for YES, one for NO
```

You can also use the CLOB API directly to query token information.

### Can I use this in a web browser?

**Yes!** The SDK works in both Node.js and browsers.

**For browser usage:**
1. Use a bundler (Vite, Webpack, Next.js, etc.)
2. Use frontend mode with wallet connection:

```typescript
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();

const sdk = new PolybasedSDK({
  trading: {
    chainId: 137,
    frontend: { signer }
  }
});
```

### Do I need USDC to trade?

**Yes!** You need USDC (a stablecoin) on the Polygon network.

**How to get USDC on Polygon:**
1. Buy USDC on an exchange (Coinbase, Binance, etc.)
2. Withdraw directly to Polygon network
3. Or bridge from Ethereum using a bridge service

**Note:** Gas fees on Polygon are very low (~$0.01 per transaction).

### How do I keep my private key secure?

**Best practices:**
- ✅ Use environment variables (`.env` file)
- ✅ Add `.env` to your `.gitignore`
- ✅ Never commit private keys to git
- ✅ Use backend mode only on secure servers
- ✅ For web apps, use frontend mode (users control their own keys)
- ❌ Never hardcode private keys in your code
- ❌ Never expose private keys in browser/frontend code

### Is there a testnet?

Polymarket operates primarily on Polygon mainnet. 

**For testing:**
- Start with small amounts (like $1-5)
- Use the data/streaming features without trading
- Test your logic thoroughly before trading large amounts

### What are the trading fees?

Trading on Polymarket includes:
- **Gas fees**: Very low on Polygon (~$0.01-0.02 per transaction)
- **Trading fees**: Check Polymarket's current fee structure
- **Spread costs**: Difference between bid and ask prices

Always start with small orders to understand the total costs.

---

## 💻 Development

### Prerequisites

- Node.js >= 18.0.0
- npm or yarn

### Setup

1. Clone the repository:
```bash
git clone https://github.com/olliegrimes123/polybased-sdk.git
cd polybased-sdk
```

2. Install dependencies:
```bash
npm install
```

3. Build the project:
```bash
npm run build
```

### Scripts

- `npm run build` — Compile TypeScript to JavaScript
- `npm run dev` — Watch mode for development
- `npm run clean` — Remove build artifacts
- `npm run examples:list` — Run the list-markets example
- `npm run examples:orderbook` — Run the live-orderbook example

### Running Examples

The `examples/` directory contains sample scripts demonstrating SDK usage:

```bash
# List all markets
npm run examples:list

# Stream live orderbook data (set POLYBASED_MARKET_ID env var)
POLYBASED_MARKET_ID=your-market-id npm run examples:orderbook
```

---

## 🚦 Rate Limiting (NEW in v0.8.0!)

The SDK includes built-in rate limiting to prevent API bans:

```typescript
import { PolybasedSDK } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// Rate limiter is automatically configured
// You can also access it directly:

// Check rate limiter stats
const stats = sdk.rateLimiter.getAllStats();
console.log(stats);

// Manually acquire a token before making requests
await sdk.rateLimiter.acquire('trading');

// Try to acquire without waiting
if (sdk.rateLimiter.tryAcquire('clob')) {
  // Make request
}

// Reset all limiters
sdk.rateLimiter.resetAll();
```

---

## 💾 Caching (NEW in v0.8.0!)

Unified caching system across all services:

```typescript
import { PolybasedSDK, CACHE_TTLS } from "@polybased/sdk";

const sdk = new PolybasedSDK();

// Cache is automatically configured
// You can access it directly:

// Get cache stats
const stats = sdk.cacheManager.getAllStats();
console.log(stats);

// Get a specific cache
const marketCache = sdk.cacheManager.getCache('markets');

// Clear all caches
sdk.cacheManager.clearAll();

// Available TTL presets
console.log(CACHE_TTLS.REALTIME);   // 5 seconds
console.log(CACHE_TTLS.FREQUENT);   // 30 seconds
console.log(CACHE_TTLS.MODERATE);   // 1 minute
console.log(CACHE_TTLS.SLOW);       // 5 minutes
console.log(CACHE_TTLS.STATIC);     // 1 hour
```

---

## 📝 Logging (NEW in v0.8.0!)

Structured logging with levels:

```typescript
import { PolybasedSDK, createLogger, LogLevel } from "@polybased/sdk";

// SDK uses logging internally
const sdk = new PolybasedSDK({ debug: true }); // Enable debug logging

// Get the SDK's logger
const logger = sdk.getLogger();

// Create custom loggers
const myLogger = createLogger({
  level: 'info',
  prefix: 'MyApp',
  timestamps: true,
  colors: true,
});

myLogger.debug('Debug message', { data: 'value' });
myLogger.info('Info message');
myLogger.warn('Warning message');
myLogger.error('Error message', { context: 'data' }, new Error('cause'));

// Create child loggers
const childLogger = myLogger.child('Trading');
childLogger.info('Trading initialized');

// Change log level
myLogger.setLevel('debug');
```

---

## 🛠️ Error Handling

The SDK includes comprehensive error handling with context and hints (enhanced in v0.8.0):

```typescript
import { 
  PolybasedSDK, 
  PolybasedError, 
  HttpError,
  TradingError,
  RateLimitError,
  AnalyticsError,
  ErrorCode,
  isHttpError,
  isRetryableError,
} from "@polybased/sdk";

try {
  const market = await sdk.getMarket("invalid-id");
} catch (error) {
  if (isHttpError(error)) {
    console.error(`HTTP ${error.status}: ${error.message}`);
    console.error(`URL: ${error.url}`);
    console.error(`Hint: ${error.hint}`); // Helpful troubleshooting hint
  } else if (error instanceof TradingError) {
    console.error(`Trading Error: ${error.message}`);
    console.error(`Order ID: ${error.orderId}`);
    console.error(`Hint: ${error.hint}`);
  } else if (error instanceof RateLimitError) {
    console.error(`Rate limited! Retry in ${error.retryAfter}ms`);
  } else if (error instanceof PolybasedError) {
    console.error(`Error ${error.code}: ${error.message}`);
    console.error(`Context:`, error.context);
    console.error(`Hint: ${error.hint}`);
  }

  // Check if error is retryable
  if (isRetryableError(error)) {
    console.log('This error can be retried');
  }

  // Get detailed error report
  if (error instanceof PolybasedError) {
    console.log(JSON.stringify(error.toJSON(), null, 2));
  }
}
```

---

## 🤝 Contributing

We welcome contributions! Here's how you can help:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

---

## 💡 About Polybased

Polybased aims to become the go-to SDK and data layer for **on-chain prediction markets**, starting with **Polymarket**.

Our goal is to make it easy for anyone — from developers to traders — to build tools that enhance their prediction-market experience.

### Current Features

| Status | Feature |
|--------|---------|
| ✅ | Polymarket data integration |
| ✅ | Real-time orderbook streaming |
| ✅ | Trading execution (CLOB) |
| ✅ | Relayer (gasless trades) |
| ✅ | Builder support |
| ✅ | AI Research (FactsAI) |
| ✅ | Smart money analytics |
| ✅ | Arbitrage detection & auto-execution |
| ✅ | Market signals |
| ✅ | Rate limiting & caching |
| ✅ | Copy trading |
| ✅ | On-chain balances & approvals |
| 🔜 | CTF operations (split/merge/redeem) |
| 🔜 | Strategy framework |

📖 **Full roadmap: [SDK_ROADMAP.md](./SDK_ROADMAP.md)**

### Follow Us

- 🐦 X (Twitter): [@polybasedxyz](https://x.com/polybasedxyz)
- 💬 Discord: [https://discord.gg/ESs57B7bcz](https://discord.gg/ESs57B7bcz)
- 🌐 Website: [https://polybased.xyz](https://polybased.xyz)

### $POLYBASED Token

Native token launched on PumpFun (Solana)  
**Contract Address:** `446tM6t3j5KngSsahHDeYzdJByG