Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
agentmc15 avatar

Trader Analysis

  • 297 installs
  • 24 repo stars
  • Updated July 31, 2026
  • agentmc15/polymarket-trader

trader-analysis is a Claude Code skill that scores Polymarket wallet addresses from on-chain trades and API history so developers building copy-trading or discovery features can rank profitable traders and track exposure

About

trader-analysis is a Polymarket-focused agent skill in agentmc15/polymarket-trader that teaches Claude to pull trader activity from Polygon on-chain logs and Polymarket APIs, then compute PnL, win rate, position exposure, and originality heuristics. The bundled Python patterns use web3.py against the CTF Exchange contract on Polygon, async HTTP clients, and a TraderAnalyzer class that filters trades by time window, scores leaderboard candidates, and flags likely copy traders versus original entrants. Developers reach for trader-analysis when wiring trader discovery, copy-trading monitors, or risk dashboards that need repeatable wallet scoring instead of one-off SQL. The skill pairs with the repo's polymarket-api and trading-strategies skills for end-to-end prediction-market tooling.

  • Polymarket position tracking
  • PnL and win-rate metrics
  • market exposure breakdown
  • trader behavior patterns
  • risk-aware trade insights

Trader Analysis by the numbers

  • 297 all-time installs (skills.sh)
  • Ranked #334 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
  • Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/agentmc15/polymarket-trader --skill trader-analysis

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs297
repo stars24
Last updatedJuly 31, 2026
Repositoryagentmc15/polymarket-trader

How do you score Polymarket traders for copy trading?

Analyze Polymarket trader positions, PnL, and market exposure to inform prediction-market strategy and risk decisions.

Who is it for?

Backend or quant developers building Polymarket copy-trading, leaderboard filters, or wallet risk monitors on Polygon.

Skip if: Developers who only need static market odds without wallet-level performance analytics or on-chain trade history.

When should I use this skill?

A developer asks to analyze Polymarket traders, rank profitable wallets, track PnL, or design copy-trading discovery logic.

What you get

Ranked trader scorecards, originality-filtered follow lists, and exposure summaries keyed to wallet addresses.

  • Trader scorecards
  • Follow-list rankings
  • Exposure summaries

By the numbers

  • Targets the Polygon CTF Exchange contract 0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E
  • Ships alongside polymarket-api and trading-strategies skills in agentmc15/polymarket-trader

Files

SKILL.mdMarkdownGitHub ↗

Trader Analysis Skill

Tracking Trader Activity

On-Chain Data

from web3 import Web3
import httpx
from typing import AsyncIterator

CTF_EXCHANGE = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E"

class TraderTracker:
    def __init__(self, polygon_rpc: str):
        self.w3 = Web3(Web3.HTTPProvider(polygon_rpc))
        self.exchange = self.w3.eth.contract(
            address=CTF_EXCHANGE,
            abi=CTF_EXCHANGE_ABI
        )
    
    async def get_trader_trades(
        self,
        address: str,
        from_block: int = None
    ) -> list[dict]:
        """Fetch all trades for an address."""
        events = self.exchange.events.OrderFilled.get_logs(
            fromBlock=from_block or "earliest",
            argument_filters={"maker": address}
        )
        
        return [self._parse_trade_event(e) for e in events]
    
    def _parse_trade_event(self, event: dict) -> dict:
        """Parse OrderFilled event into trade dict."""
        return {
            "tx_hash": event.transactionHash.hex(),
            "block_number": event.blockNumber,
            "maker": event.args.maker,
            "taker": event.args.taker,
            "token_id": str(event.args.tokenId),
            "amount": event.args.amount / 1e6,  # Assuming 6 decimals
            "price": event.args.price / 1e18,
            "side": "BUY" if event.args.side == 0 else "SELL",
            "timestamp": self._get_block_timestamp(event.blockNumber)
        }

Polymarket Data API

class PolymarketDataClient:
    BASE_URL = "https://data-api.polymarket.com"
    
    def __init__(self):
        self.client = httpx.AsyncClient(
            base_url=self.BASE_URL,
            timeout=30.0
        )
    
    async def get_trader_profile(self, address: str) -> dict:
        """Fetch trader profile and stats."""
        response = await self.client.get(f"/users/{address}")
        response.raise_for_status()
        return response.json()
    
    async def get_trader_positions(self, address: str) -> list[dict]:
        """Get all positions for a trader."""
        response = await self.client.get(
            "/positions",
            params={"user": address}
        )
        response.raise_for_status()
        return response.json()
    
    async def get_trader_activity(
        self,
        address: str,
        limit: int = 100,
        offset: int = 0
    ) -> list[dict]:
        """Get recent trading activity."""
        response = await self.client.get(
            "/activity",
            params={
                "user": address,
                "limit": limit,
                "offset": offset
            }
        )
        response.raise_for_status()
        return response.json()
    
    async def get_leaderboard(
        self,
        period: str = "all",
        limit: int = 100
    ) -> list[dict]:
        """Get top traders by P&L."""
        response = await self.client.get(
            "/leaderboard",
            params={"period": period, "limit": limit}
        )
        response.raise_for_status()
        return response.json()

Trader Scoring System

from dataclasses import dataclass
from datetime import datetime, timedelta
import numpy as np
from typing import Optional

@dataclass
class TraderMetrics:
    address: str
    total_pnl: float
    realized_pnl: float
    unrealized_pnl: float
    win_rate: float
    avg_return_per_trade: float
    sharpe_ratio: float
    total_trades: int
    unique_markets: int
    avg_position_size: float
    avg_hold_time: timedelta
    consistency_score: float
    recency_score: float
    largest_win: float
    largest_loss: float
    profit_factor: float  # gross profit / gross loss

class TraderAnalyzer:
    def __init__(self, data_client: PolymarketDataClient):
        self.client = data_client
    
    async def analyze_trader(
        self,
        address: str,
        days: int = 90
    ) -> TraderMetrics:
        """Comprehensive trader analysis."""
        activity = await self.client.get_trader_activity(
            address, limit=1000
        )
        positions = await self.client.get_trader_positions(address)
        
        # Filter to time period
        cutoff = datetime.utcnow() - timedelta(days=days)
        recent_trades = [
            t for t in activity
            if datetime.fromisoformat(t["timestamp"]) > cutoff
        ]
        
        return TraderMetrics(
            address=address,
            total_pnl=self._calculate_total_pnl(positions, recent_trades),
            realized_pnl=self._calculate_realized_pnl(recent_trades),
            unrealized_pnl=self._calculate_unrealized_pnl(positions),
            win_rate=self._calculate_win_rate(recent_trades),
            avg_return_per_trade=self._calculate_avg_return(recent_trades),
            sharpe_ratio=self._calculate_sharpe(recent_trades),
            total_trades=len(recent_trades),
            unique_markets=len(set(t["market_id"] for t in recent_trades)),
            avg_position_size=self._calculate_avg_size(recent_trades),
            avg_hold_time=self._calculate_avg_hold_time(recent_trades),
            consistency_score=self._calculate_consistency(recent_trades),
            recency_score=self._calculate_recency_score(recent_trades),
            largest_win=max((t.get("pnl", 0) for t in recent_trades), default=0),
            largest_loss=min((t.get("pnl", 0) for t in recent_trades), default=0),
            profit_factor=self._calculate_profit_factor(recent_trades)
        )
    
    def _calculate_win_rate(self, trades: list[dict]) -> float:
        """Calculate percentage of profitable trades."""
        if not trades:
            return 0
        
        winning = sum(1 for t in trades if t.get("pnl", 0) > 0)
        return winning / len(trades)
    
    def _calculate_sharpe(self, trades: list[dict]) -> float:
        """Calculate Sharpe ratio of returns."""
        returns = [t.get("return_pct", 0) for t in trades if "return_pct" in t]
        
        if len(returns) < 2:
            return 0
        
        mean_return = np.mean(returns)
        std_return = np.std(returns)
        
        if std_return == 0:
            return 0
        
        # Annualize assuming daily trades
        return (mean_return * 365**0.5) / std_return
    
    def _calculate_consistency(self, trades: list[dict]) -> float:
        """Score how consistent the trader's performance is."""
        if len(trades) < 10:
            return 0
        
        # Group by week
        weekly_pnl = {}
        for trade in trades:
            week = datetime.fromisoformat(trade["timestamp"]).isocalendar()[:2]
            weekly_pnl[week] = weekly_pnl.get(week, 0) + trade.get("pnl", 0)
        
        if len(weekly_pnl) < 4:
            return 0
        
        # Calculate consistency as % of profitable weeks
        profitable_weeks = sum(1 for pnl in weekly_pnl.values() if pnl > 0)
        return profitable_weeks / len(weekly_pnl)
    
    def _calculate_recency_score(self, trades: list[dict]) -> float:
        """Score based on recent activity (more recent = higher)."""
        if not trades:
            return 0
        
        latest = max(
            datetime.fromisoformat(t["timestamp"]) for t in trades
        )
        days_since = (datetime.utcnow() - latest).days
        
        # Decay score over 30 days
        return max(0, 1 - (days_since / 30))
    
    def _calculate_profit_factor(self, trades: list[dict]) -> float:
        """Gross profit / gross loss."""
        gross_profit = sum(t.get("pnl", 0) for t in trades if t.get("pnl", 0) > 0)
        gross_loss = abs(sum(t.get("pnl", 0) for t in trades if t.get("pnl", 0) < 0))
        
        if gross_loss == 0:
            return float('inf') if gross_profit > 0 else 0
        
        return gross_profit / gross_loss


class TraderScorer:
    def __init__(self, weights: dict = None):
        self.weights = weights or {
            "pnl": 0.20,
            "win_rate": 0.15,
            "sharpe": 0.15,
            "consistency": 0.15,
            "recency": 0.10,
            "profit_factor": 0.10,
            "experience": 0.10,
            "diversity": 0.05
        }
    
    def calculate_score(self, metrics: TraderMetrics) -> float:
        """Calculate overall trader score (0-100)."""
        scores = {
            "pnl": self._normalize_pnl(metrics.total_pnl),
            "win_rate": metrics.win_rate * 100,
            "sharpe": self._normalize_sharpe(metrics.sharpe_ratio),
            "consistency": metrics.consistency_score * 100,
            "recency": metrics.recency_score * 100,
            "profit_factor": self._normalize_profit_factor(metrics.profit_factor),
            "experience": self._normalize_trades(metrics.total_trades),
            "diversity": self._normalize_markets(metrics.unique_markets)
        }
        
        return sum(scores[k] * self.weights[k] for k in self.weights)
    
    def _normalize_pnl(self, pnl: float) -> float:
        """Normalize P&L to 0-100 scale."""
        if pnl <= 0:
            return max(0, 50 + pnl / 1000)
        return min(100, 50 + np.log1p(pnl) * 8)
    
    def _normalize_sharpe(self, sharpe: float) -> float:
        """Normalize Sharpe ratio to 0-100."""
        # Sharpe of 2+ is excellent
        return min(100, max(0, sharpe * 33))
    
    def _normalize_profit_factor(self, pf: float) -> float:
        """Normalize profit factor to 0-100."""
        if pf == float('inf'):
            return 100
        # PF of 2+ is good
        return min(100, pf * 40)
    
    def _normalize_trades(self, trades: int) -> float:
        """Normalize trade count to 0-100."""
        # 100+ trades shows experience
        return min(100, trades)
    
    def _normalize_markets(self, markets: int) -> float:
        """Normalize unique markets to 0-100."""
        # Trading 10+ markets shows diversity
        return min(100, markets * 10)

Finding Traders to Follow

class TraderDiscovery:
    def __init__(
        self,
        data_client: PolymarketDataClient,
        analyzer: TraderAnalyzer
    ):
        self.client = data_client
        self.analyzer = analyzer
        self.scorer = TraderScorer()
    
    async def find_top_traders(
        self,
        min_trades: int = 50,
        min_pnl: float = 1000,
        min_win_rate: float = 0.5,
        days: int = 30
    ) -> list[tuple[str, float, TraderMetrics]]:
        """Discover top performing traders."""
        leaderboard = await self.client.get_leaderboard(limit=500)
        
        candidates = []
        for trader in leaderboard:
            try:
                metrics = await self.analyzer.analyze_trader(
                    trader["address"],
                    days=days
                )
                
                # Apply filters
                if (metrics.total_trades >= min_trades and
                    metrics.total_pnl >= min_pnl and
                    metrics.win_rate >= min_win_rate):
                    
                    score = self.scorer.calculate_score(metrics)
                    candidates.append((trader["address"], score, metrics))
            except Exception as e:
                # Skip traders with errors
                continue
        
        return sorted(candidates, key=lambda x: x[1], reverse=True)
    
    async def find_market_specialists(
        self,
        market_category: str,
        min_trades_in_category: int = 20
    ) -> list[str]:
        """Find traders who specialize in specific market categories."""
        # Implementation would query by category
        pass
    
    async def find_original_traders(
        self,
        min_originality_score: float = 0.7
    ) -> list[str]:
        """
        Find traders who make original trades (not copy trading).
        
        Originality is measured by:
        - Trade timing (not consistently after other traders)
        - Position uniqueness (not mirroring others)
        - Contrarian indicators
        """
        leaderboard = await self.client.get_leaderboard(limit=200)
        original_traders = []
        
        for trader in leaderboard:
            activity = await self.client.get_trader_activity(
                trader["address"],
                limit=100
            )
            
            originality = await self._calculate_originality(activity)
            
            if originality >= min_originality_score:
                original_traders.append(trader["address"])
        
        return original_traders
    
    async def _calculate_originality(
        self,
        trades: list[dict]
    ) -> float:
        """Calculate how original a trader's trades are."""
        # Compare trade timing with market average
        # Check for unique position entries
        # Measure contrarian behavior
        return 0.5  # Placeholder


class CopyTradingManager:
    def __init__(
        self,
        data_client: PolymarketDataClient,
        trading_service,  # Your trading service
        config: dict
    ):
        self.client = data_client
        self.trading = trading_service
        self.tracked_traders: dict[str, dict] = {}
        self.copy_delay = config.get("copy_delay_seconds", 30)
        self.size_multiplier = config.get("size_multiplier", 0.25)
        self.max_position_pct = config.get("max_position_pct", 0.1)
    
    def add_trader(
        self,
        address: str,
        multiplier: float = None,
        markets: list[str] = None
    ):
        """Add a trader to copy."""
        self.tracked_traders[address] = {
            "multiplier": multiplier or self.size_multiplier,
            "markets": markets,  # None = all markets
            "last_trade": None
        }
    
    def remove_trader(self, address: str):
        """Stop copying a trader."""
        self.tracked_traders.pop(address, None)
    
    async def process_trade(self, trade: dict):
        """Process a trade from tracked trader."""
        address = trade["trader_address"]
        
        if address not in self.tracked_traders:
            return
        
        config = self.tracked_traders[address]
        
        # Check market filter
        if config["markets"] and trade["market_id"] not in config["markets"]:
            return
        
        # Wait for delay
        await asyncio.sleep(self.copy_delay)
        
        # Calculate size
        size = trade["size"] * config["multiplier"]
        
        # Apply max position limit
        portfolio = await self.trading.get_portfolio()
        max_size = portfolio["value"] * self.max_position_pct / trade["price"]
        size = min(size, max_size)
        
        # Execute copy trade
        await self.trading.place_order(
            token_id=trade["token_id"],
            side=trade["side"],
            price=trade["price"],
            size=size,
            metadata={"copy_source": address}
        )

Real-Time Monitoring

import asyncio
from collections import defaultdict
from typing import Callable, Awaitable

class LiveTraderMonitor:
    def __init__(self, tracked_addresses: list[str]):
        self.tracked = set(tracked_addresses)
        self.callbacks: dict[str, list[Callable]] = defaultdict(list)
        self._running = False
    
    def on_trade(self, callback: Callable[[dict], Awaitable[None]]):
        """Register callback for trade events."""
        self.callbacks["trade"].append(callback)
        return callback
    
    def on_position_change(self, callback: Callable[[dict], Awaitable[None]]):
        """Register callback for position changes."""
        self.callbacks["position"].append(callback)
        return callback
    
    async def start(self):
        """Start monitoring tracked traders."""
        self._running = True
        
        async for event in self._watch_events():
            if not self._running:
                break
            
            if event.get("trader") in self.tracked:
                event_type = event.get("type", "trade")
                
                for callback in self.callbacks[event_type]:
                    try:
                        await callback(event)
                    except Exception as e:
                        print(f"Callback error: {e}")
    
    def stop(self):
        """Stop monitoring."""
        self._running = False
    
    def add_trader(self, address: str):
        """Add trader to watch list."""
        self.tracked.add(address)
    
    def remove_trader(self, address: str):
        """Remove trader from watch list."""
        self.tracked.discard(address)
    
    async def _watch_events(self):
        """Watch for on-chain events."""
        # Implementation would use WebSocket or polling
        while self._running:
            # Poll for new events
            await asyncio.sleep(5)
            yield {}  # Placeholder

Related skills

How it compares

Pick trader-analysis when you need wallet scoring and copy-trading discovery logic rather than only market-price or order-placement API helpers.

FAQ

What data does trader-analysis use?

trader-analysis combines Polymarket API trader history with Polygon on-chain reads through web3.py, targeting the CTF Exchange contract address for exchange-level trade events and merging them into wallet-level metrics.

Can trader-analysis find traders to copy?

trader-analysis includes discovery flows that pull leaderboard candidates, score performance, and apply originality heuristics so developers can shortlist wallets that are less likely to be mirroring other traders.

What language does trader-analysis assume?

trader-analysis documents async Python patterns with web3.py, httpx, and typed iterators, so implementers should run Polygon RPC access and async HTTP clients in the same stack.

Finance & Tradingfinancepricing

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.