Skill: hanswuhan/polymarket-trade-agent (version 1.0.1)

# Polymarket Trade Agent Skill ## Overview A fully functional Polymarket trading agent that can research markets, analyze opportunities, and execute trades. ## Configuration Required - **POLYMARKET_PRIVATE_KEY**: Your Polygon wallet private key (starts with 0x...) - **POLYMARKET_FUNDER_ADDRESS**: Optional funder address for transfers (starts with 0x...) - This key controls your Polymarket account and must have USDC balance ## Setup ### Step 1: Get Your Private Key 1. Go to [polymarket.com](https://polymarket.com) 2. Connect your wallet (MetaMask) 3. Go to Settings → API Keys or find your wallet address 4. Get the private key for the wallet that has USDC on Polymarket ### Step 2: Configure ```bash export POLYMARKET_PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE ``` ### Step 3: Check Balance ```bash polymarket balance ``` ## Commands ### Research & Analysis - `polymarket markets` - List top markets - `polymarket search <query>` - Search markets - `polymarket analyze <market_id>` - Get AI analysis ### Trading - `polymarket balance` - Check USDC balance - `polymarket buy <token_id> <price> <size>` - Place buy order - `polymarket sell <token_id> <price> <size>` - Place sell order - `polymarket positions` - View open positions ### Utility - `polymarket doctor` - Health check - `polymarket address` - Show your wallet address ## Trading Strategy ### Finding Opportunities 1. Run `polymarket markets` to see trending markets 2. Research the underlying events via web search 3. Compare market odds to your own probability estimate 4. Calculate edge: `Edge = Your_Probability - Market_Probability` ### Example Analysis ``` Market: "Will Iran regime fall by June 30?" Current Price: Yes = $0.42 (42% implied) My Research: Given Khamenei killed, power vacuum exists My Estimate: 60% probability Edge: +18% → BUY YES ``` ### Position Sizing - Never risk more than 5% of bankroll on single trade - Recommended: 2-5% per trade - Diversify across uncorrelated events ## Risk Rules 1. Always verify balance before trading 2. Start with small positions (test the waters) 3. Don't bet more than you can afford to lose 4. Set mental stop-loss at 50% of position ## Notes - All trades are on Polygon network - Minimum trade size typically $1 - Fees are small but apply to each trade

path: _meta.json
content: ownerId: k9775g6hx431npsef0pzv1smn1822wtc
slug: polymarket-trade-agent
version: 1.0.0
publishedAt: 1740847322000

path: cli.py
content: #!/usr/bin/env python3
"""
Polymarket Trade Agent CLI
Usage: polymarket <command>
"""
import os
import sys
from typing import Optional
import typer
from rich.console import Console
from rich.table import Table
from rich.panel import Panel

# Add current directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

import trade

app = typer.Typer(
    name="polymarket",
    help="🎰 Polymarket Trade Agent",
    rich_markup_mode="markdown",
)
console = Console()


@app.command()
def address():
    """Show your wallet address"""
    addr = trade.get_wallet_address()
    funder = trade.get_funder_address()
    if addr:
        content = f"[bold green]{addr}[/bold green]"
        if funder:
            content += f"\n\n[bold]Funder:[/bold] {funder}"
        console.print(Panel(
            content,
            title="💼 Wallet Address",
            border_style="cyan"
        ))
    else:
        console.print("[red]Could not get address. Check POLYMARKET_PRIVATE_KEY[/red]")


@app.command()
def balance():
    """Check your USDC balance"""
    bal = trade.get_balance()
    if bal is not None:
        console.print(Panel(
            f"[bold green]${bal:.2f} USDC[/bold green]",
            title="💵 Balance",
            border_style="green"
        ))
    else:
        console.print("[red]Failed to get balance[/red]")


@app.command()
def markets(
    limit: int = typer.Option(10, "--limit", "-n", help="Number of markets to show"),
    search: Optional[str] = typer.Option(None, "--search", "-s", help="Search markets"),
):
    """List active markets"""
    markets = trade.get_markets(limit=limit, search=search)
    if not markets:
        console.print("[red]No markets found[/red]")
        return
    
    table = Table(title="📊 Polymarket Markets")
    table.add_column("Question", style="cyan", max_width=50)
    table.add_column("Yes", justify="right")
    table.add_column("No", justify="right")
    table.add_column("Volume", justify="right")
    
    for m in markets:
        question = m.get("question", "Unknown")[:45]
        yes = m.get("yes_price", "N/A")
        no = m.get("no_price", "N/A")
        volume = m.get("volume", "0")
        try:
            vol = float(volume)
            if vol > 1e6:
                volume = f"${vol/1e6:.1f}M"
            elif vol > 1e3:
                volume = f"${vol/1e3:.1f}K"
            else:
                volume = f"${vol:.0f}"
        except:
            pass
        table.add_row(question, f"${yes}", f"${no}", volume)
    
    console.print(table)


@app.command()
def search(query: str):
    """Search for markets"""
    markets = trade.get_markets(limit=20, search=query)
    if not markets:
        console.print(f"[yellow]No markets found for: {query}[/yellow]")
        return
    
    table = Table(title=f"🔍 Search Results: {query}")
    table.add_column("Question", style="cyan", max_width=50)
    table.add_column("Yes", justify="right")
    table.add_column("Volume", justify="right")
    
    for m in markets:
        question = m.get("question", "Unknown")[:45]
        yes = m.get("yes_price", "N/A")
        volume = m.get("volume", "0")
        try:
            vol = float(volume)
            if vol > 1e6:
                volume = f"${vol/1e6:.1f}M"
            elif vol > 1e3:
                volume = f"${vol/1e3:.1f}K"
            else:
                volume = f"${vol:.0f}"
        except:
            pass
        table.add_row(question, f"${yes}", volume)
    
    console.print(table)


@app.command()
def buy(
    token_id: str = typer.Argument(..., help="Token ID"),
    price: float = typer.Argument(..., help="Price to buy at"),
    size: float = typer.Argument(..., help="Size in USDC"),
):
    """Buy YES on a market"""
    console.print(f"[cyan]Buying YES: {size} @ ${price}[/cyan]")
    result = trade.place_order(token_id, "BUY", price, size)
    if result:
        console.print(Panel(
            f"[green]Order placed successfully![/green]\n{result}",
            title="✅ Success",
            border_style="green"
        ))
    else:
        console.print("[red]Order failed[/red]")


@app.command()
def sell(
    token_id: str = typer.Argument(..., help="Token ID"),
    price: float = typer.Argument(..., help="Price to sell at"),
    size: float = typer.Argument(..., help="Size in USDC"),
):
    """Sell YES on a market"""
    console.print(f"[cyan]Selling YES: {size} @ ${price}[/cyan]")
    result = trade.place_order(token_id, "SELL", price, size)
    if result:
        console.print(Panel(
            f"[green]Order placed successfully![/green]\n{result}",
            title="✅ Success",
            border_style="green"
        ))
    else:
        console.print("[red]Order failed[/red]")


@app.command()
def positions():
    """View your open positions"""
    pos = trade.get_positions()
    if pos:
        console.print(pos)
    else:
        console.print("[yellow]No positions found[/yellow]")


@app.command()
def doctor():
    """Health check"""
    console.print(Panel(
        "[bold]Polymarket Trade Agent Health Check[/bold]",
        border_style="cyan"
    ))
    
    # Check Python
    console.print("✓ Python")
    
    # Check private key
    key = os.getenv("POLYMARKET_PRIVATE_KEY")
    if key:
        console.print(f"✓ POLYMARKET_PRIVATE_KEY set")
        addr = trade.get_wallet_address()
        console.print(f"  Address: {addr}")
    else:
        console.print("✗ POLYMARKET_PRIVATE_KEY not set")
    
    # Check balance
    bal = trade.get_balance()
    if bal is not None:
        console.print(f"✓ Balance: ${bal:.2f} USDC")
    else:
        console.print("✗ Could not fetch balance")


@app.command()
def setup():
    """Setup instructions"""
    console.print(Panel(
        """[bold]Setup Instructions:[/bold]

1. Get your Polygon private key from MetaMask:
   - Open MetaMask
   - Click 3 dots → Account details
   - Export private key

2. Set the environment variable:
   [cyan]export POLYMARKET_PRIVATE_KEY=0xYOUR_KEY_HERE[/cyan]

3. Check balance:
   [cyan]polymarket balance[/cyan]

4. Fund your account:
   - Go to Polymarket.com
   - Deposit USDC to your address
   - Use: polymarket address
""",
        title="⚙️ Setup",
        border_style="green"
    ))


if __name__ == "__main__":
    app()


path: polymarket_trade_agent.egg-info/SOURCES.txt
content: SKILL.md
cli.py
pyproject.toml
polymarket_trade_agent.egg-info/PKG-INFO
polymarket_trade_agent.egg-info/SOURCES.txt
polymarket_trade_agent.egg-info/dependency_links.txt
polymarket_trade_agent.egg-info/entry_points.txt
polymarket_trade_agent.egg-info/requires.txt
polymarket_trade_agent.egg-info/top_level.txt

path: polymarket_trade_agent.egg-info/dependency_links.txt
content: 


path: polymarket_trade_agent.egg-info/entry_points.txt
content: [console_scripts]
polymarket = cli:app


path: polymarket_trade_agent.egg-info/requires.txt
content: py-clob-client>=0.34.0
requests>=2.28.0
rich>=13.0.0
typer>=0.9.0
eth-account>=0.9.0
web3>=6.0.0


path: polymarket_trade_agent.egg-info/top_level.txt
content: cli


path: pyproject.toml
content: [build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "polymarket-trade-agent"
version = "0.2.0"
description = "Polymarket trading agent with complete implementation"
readme = "SKILL.md"
requires-python = ">=3.9"
dependencies = [
    "py-clob-client>=0.34.0",
    "requests>=2.28.0",
    "rich>=13.0.0",
    "typer>=0.9.0",
    "eth-account>=0.9.0",
    "web3>=6.0.0",
]

[project.scripts]
polymarket = "cli:app"

[tool.setuptools]
py-modules = ["cli"]


path: requirements.txt
content: py-clob-client>=0.34.0
requests>=2.28.0
rich>=13.0.0
typer>=0.9.0
eth-account>=0.9.0
web3>=6.0.0
python-dotenv>=1.0.0


path: trade.py
content: """
Polymarket Trading Functions
"""
import os
import sys
from typing import List, Optional, Dict, Union
from eth_account import Account
from py_clob_client.client import ClobClient
from py_clob_client.clob_types import BalanceAllowanceParams, AssetType, OrderArgs
from py_clob_client.order_builder.constants import BUY, SELL
from rich.console import Console

console = Console()

# Environment variable names
PRIVATE_KEY_ENV = "POLYMARKET_PRIVATE_KEY"
FUNDER_ADDRESS_ENV = "POLYMARKET_FUNDER_ADDRESS"

# Data API
DATA_API_BASE_URL = "https://data-api.polymarket.com"
POSITIONS_ENDPOINT = "/positions"
DEFAULT_POSITIONS_LIMIT = 50


def get_private_key() -> Optional[str]:
    """Get private key from environment"""
    key = os.getenv(PRIVATE_KEY_ENV)
    if not key:
        console.print(f"[red]Error: {PRIVATE_KEY_ENV} not set[/red]")
        console.print("Run: export POLYMARKET_PRIVATE_KEY=0x...")
        return None
    if not key.startswith("0x"):
        key = "0x" + key
    return key


def get_funder_address() -> Optional[str]:
    """Get funder address from environment"""
    return os.getenv(FUNDER_ADDRESS_ENV)


def get_wallet_address() -> Optional[str]:
    """Get wallet address from private key"""
    key = get_private_key()
    if not key:
        return None
    try:
        acct = Account.from_key(key)
        return acct.address
    except Exception as e:
        console.print(f"[red]Error deriving address: {e}[/red]")
        return None


def fetch_positions_from_data_api(user: str, limit: int = DEFAULT_POSITIONS_LIMIT) -> List[Dict]:
    """Fetch positions from Polymarket Data API"""
    import requests

    url = f"{DATA_API_BASE_URL}{POSITIONS_ENDPOINT}"
    params = {"user": user, "limit": limit}
    resp = requests.get(url, params=params, timeout=10)
    resp.raise_for_status()
    data = resp.json()
    if not isinstance(data, list):
        raise RuntimeError("Unexpected response from Polymarket data API")
    return data


def get_client() -> Optional[ClobClient]:
    """Get authenticated Polymarket client"""
    key = get_private_key()
    funder = get_funder_address()
    if not key:
        return None
    
    try:
        host = "https://clob.polymarket.com"
        chain_id = 137  # Polygon
        
        console.print(f"[cyan]Initializing ClobClient:[/cyan]")
        console.print(f"  - Host: {host}")
        console.print(f"  - Chain ID: {chain_id}")
        console.print(f"  - Key (first 10 chars): {key[:10]}...")
        console.print(f"  - Funder: {funder}")
        
        # Create client with key and optional funder address
        signature_type = int(os.environ.get("POLYMARKET_SIGNATURE_TYPE", "1"))
        client = ClobClient(
            host,
            key=key,
            chain_id=chain_id,
            funder=funder,
            signature_type=signature_type
        )

        try:
            creds = client.create_or_derive_api_creds()
            client.set_api_creds(creds)
            console.print("[green]  - API credentials: derived successfully[/green]")
        except Exception as e:
            console.print(f"[red]Failed to derive API credentials: {e}[/red]")
            raise
        
        return client
    except Exception as e:
        console.print(f"[red]Error creating client: {e}[/red]")
        return None


def _from_raw_units(raw_value: Union[str, int, float], decimals: int = 6) -> float:
    """Convert raw token units to float value."""
    try:
        val = float(raw_value)
        return val / (10 ** decimals)
    except (ValueError, TypeError):
        return 0.0


def get_balance() -> Optional[float]:
    """Get USDC balance via ClobClient"""
    client = get_client()
    if not client:
        return None

    try:
        params = BalanceAllowanceParams(asset_type=AssetType.COLLATERAL)
        resp = client.get_balance_allowance(params=params)
        raw_balance = resp.get("balance", 0)
        return _from_raw_units(raw_balance)
    except Exception as e:
        console.print(f"[red]Failed to get USDC balance: {e}[/red]")
        raise


def get_markets(limit: int = 10, search: str = None):
    """Get list of markets"""
    client = get_client()
    if not client:
        return None
    
    try:
        markets = client.get_markets(
            limit=limit,
            search=search,
        )
        return markets
    except Exception as e:
        console.print(f"[red]Error fetching markets: {e}[/red]")
        return None


def get_market(token_id: str):
    """Get specific market details"""
    client = get_client()
    if not client:
        return None
    
    try:
        market = client.get_market(token_id)
        return market
    except Exception as e:
        console.print(f"[red]Error fetching market: {e}[/red]")
        return None


def place_order(token_id: str, side: str, price: float, size: float):
    """Place a trade order"""
    client = get_client()
    if not client:
        return None
    
    order_side = BUY if side.upper() == "BUY" else SELL
    
    try:
        resp = client.create_and_post_order(OrderArgs(
            token_id=token_id,
            price=float(price),
            size=float(size),
            side=order_side
        ))
        return resp
    except Exception as e:
        console.print(f"[red]Trade failed: {e}[/red]")
        return None


def get_positions(include_closed: bool = False):
    """Get positions via Polymarket Data API"""
    funder = get_funder_address()
    if not funder:
        raise RuntimeError("POLYMARKET_FUNDER_ADDRESS is not set")

    positions = fetch_positions_from_data_api(funder)
    if not positions:
        raise RuntimeError("Polymarket Data API returned no positions")

    return positions


def get_order_book(token_id: str):
    """Get order book for a market"""
    client = get_client()
    if not client:
        return None
    
    try:
        order_book = client.get_order_book(token_id)
        return order_book
    except Exception as e:
        console.print(f"[red]Error fetching order book: {e}[/red]")
        return None


def get_price(token_id: str, side: str = "BUY") -> Optional[float]:
    """Get current price for a market"""
    client = get_client()
    if not client:
        return None
    
    try:
        price = client.get_price(token_id)
        return price
    except Exception as e:
        console.print(f"[red]Error fetching price: {e}[/red]")
        return None


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)