Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/deniszidbaev-cmyk/polyclaw-trading/llms.txt

Use this file to discover all available pages before exploring further.

PolyClaw Trading includes a copy-trading layer that discovers active Polymarket wallets from public APIs, scores them by historical performance metrics, and generates advisory signals based on their recent trades. The entire pipeline runs without any API key — it uses only Polymarket’s public data-api endpoints. Signals are written to runtime/ JSON files and are consumed by the LLM orchestration layer as advisory context.
Copy-trading signals are advisory only. They feed into the LLM orchestration layer for contextual awareness. They are not execution commands and they do not pass through the deterministic execution gate (validateexecute). A copy signal alone will never place an order.

Components

The copy-trading pipeline consists of three scripts that work in sequence:

trader_tracker.py — Discover and track wallets

Queries https://data-api.polymarket.com/trades and https://data-api.polymarket.com/positions to discover active wallets and aggregate their activity. No API key is required. For each discovered wallet it computes:
  • trade_count — total number of observed trades
  • total_notional — sum of |size| × |price| across all trades
  • distinct_markets — number of unique condition IDs traded
  • market_concentration — share of total notional in the single largest market (1.0 = fully concentrated)
  • last_seen_age_seconds — seconds since the wallet’s most recent observed trade
When enrichment is enabled (TRADER_TRACKER_ENRICH=1), the tracker also fetches the wallet’s open and historical positions from data-api and derives real win-rate, maximum drawdown, and a consistency score from percentPnl per position. Unverified wallets use conservative defaults (win_rate=0.0, max_drawdown_pct=100.0) that keep them below the copy threshold.
uv run python trader_tracker.py
Output files:
  • runtime/wallet_watchlist.json — scored wallet list
  • runtime/copy_wallet_signals.json — recent trades from tracked wallets

wallet_scoring.py — Score individual wallets

Provides the score_wallet(wallet) function used by trader_tracker.py and copy_trading.py. The score combines five weighted components:
ComponentMax contributionFormula
Win rate0.40min(win_rate / 100, 0.40)
Consistency0.25min(consistency, 0.25)
Trade volume0.20min(trade_count / 1000, 0.20)
Low drawdown0.10min((40 - max_drawdown_pct) / 400, 0.10)
Diversification0.05min((0.8 - market_concentration) * 1.25, 0.05)
The score is clamped to [0.0, 1.0]. Wallets with all-default (unverified) values score 0.0. The copy-trading threshold in copy_trading.py is 0.45 — only wallets that score at or above this value generate actionable signals.

copy_trading.py — Generate copy-trade decisions

A library module (not a CLI script) that exposes generate_fallback_decisions(). It reads the watchlist from runtime/wallet_watchlist.json, scores each wallet with wallet_scoring.score_wallet, filters to those at or above the 0.45 threshold, then reads runtime/copy_wallet_signals.json and normalizes qualifying signals into copy-trade decisions. Each decision passes through copy_signal_normalizer.normalize_copy_signal, which enforces a liquidity floor, a maximum signal age, and a maximum trade size (max_trade_usd, default 1.0). Signals that fail any of these checks are discarded. This function is called internally by the LLM orchestration layer — it is not invoked directly from the command line.

CoinGecko spot prices

A separate public data adapter fetches spot prices from CoinGecko (no API key required) and writes them to runtime/spot_prices.json. Configure the assets to track with COINGECKO_IDS:
# .env.local
COINGECKO_IDS=bitcoin,ethereum,solana
The default value tracks Bitcoin, Ethereum, and Solana. Spot prices are used for context in the LLM orchestration layer.

Configuration

All copy-trading behavior is controlled through environment variables. Copy from .env.example and set values in .env.local:
VariableDefaultDescription
TRADER_TRACKER_TRADES_LIMIT500Number of recent trades to fetch from data-api per refresh
TRADER_TRACKER_MIN_TRADES2Minimum observed trades for a wallet to enter the watchlist
TRADER_TRACKER_ENRICH1Enable position-based PnL enrichment (0 to disable)
TRADER_TRACKER_ENRICH_MAX20Maximum number of wallets to enrich per refresh (budget control)
TRADER_TRACKER_SIGNAL_USD1.0Suggested USD amount attached to each copy signal
TRADER_TRACKER_MAX_SIGNAL_AGE_SEC1800Discard signals older than this many seconds (default 30 minutes)
COINGECKO_IDSbitcoin,ethereum,solanaComma-separated CoinGecko asset IDs for spot price tracking
All of these can also be passed as CLI arguments to trader_tracker.py:
uv run python trader_tracker.py \
  --trades-limit 500 \
  --min-trades 2 \
  --enrich-max 20 \
  --signal-usd 1.0 \
  --max-signal-age 1800

# Disable enrichment (faster, no position API calls):
uv run python trader_tracker.py --no-enrich

Running the copy-trading pipeline

trader_tracker.py is the only executable script in the copy-trading pipeline. It calls wallet_scoring.score_wallet and the copy_trading.generate_fallback_decisions helper internally — neither wallet_scoring.py nor copy_trading.py is a standalone CLI script. Run the tracker to refresh both output files:
uv run python trader_tracker.py
After running, inspect the output files:
cat runtime/wallet_watchlist.json
cat runtime/copy_wallet_signals.json

Output file format

runtime/wallet_watchlist.json

{
  "generated_at": "2026-01-01T12:00:00Z",
  "source": {
    "provider": "Polymarket data-api",
    "endpoint": "https://data-api.polymarket.com/trades",
    "enriched": true
  },
  "wallets": [
    {
      "address": "0xabc...",
      "win_rate": 62.5,
      "consistency": 0.21,
      "max_drawdown_pct": 18.3,
      "market_concentration": 0.34,
      "trade_count": 87,
      "total_notional": 412.5,
      "distinct_markets": 14,
      "last_seen_age_seconds": 340
    }
  ]
}

runtime/copy_wallet_signals.json

{
  "generated_at": "2026-01-01T12:00:00Z",
  "source": {
    "provider": "Polymarket data-api",
    "endpoint": "https://data-api.polymarket.com/trades"
  },
  "signals": [
    {
      "wallet": "0xabc...",
      "age_seconds": 120,
      "liquidity_hint": 45.0,
      "suggested_amount_usd": 1.0,
      "side": "BUY",
      "asset": "...",
      "condition_id": "0x...",
      "outcome": "YES",
      "price": 0.63,
      "title": "Will BTC exceed $100k by end of month?"
    }
  ]
}
Signals are sorted by age_seconds (most recent first). Any signal older than TRADER_TRACKER_MAX_SIGNAL_AGE_SEC seconds is excluded from the output.

Safety properties

The tracker is written to never abort the pipeline on a transient network error — all writes are best-effort. A failure to fetch positions for one wallet does not cancel enrichment for the rest of the batch; the affected wallet receives an enrich_error field in the watchlist and retains its conservative defaults. The entire refresh() call is wrapped in a broad exception handler so a data-source outage never propagates to the main pipeline loop.

Build docs developers (and LLMs) love