Skill: skybinjf/momentum-polymarket-trader (version 1.0.0)

--- name: momentum-polymarket-trader description: Automated Polymarket trading skill based on price momentum signals—executes trades when market trends are detected metadata: author: "AION Trader" version: "1.0.0" displayName: "Momentum Polymarket Trader" difficulty: "intermediate" --- # Momentum Polymarket Trader An automated trading skill that executes trades on Polymarket based on price momentum and market trend signals. ## Features - 🎯 Automatically monitors opportunity markets on Polymarket - 📊 Analyzes price momentum and trend signals - ⚠️ Built-in risk management and flip-flop detection - 🔄 Auto-redeems winnings from resolved markets - 📝 Detailed reasoning logs for every trade ## How It Works This skill runs every 15 minutes and: 1. **Fetches Market Briefing** - Gets current opportunity markets from AION API 2. **Risk Check** - Checks for risk alerts; pauses trading if any exist 3. **Market Analysis** - For each candidate market: - Fetches market context and trading discipline data - Calculates price momentum indicators - Evaluates trading edge 4. **Executes Trades** - Trades when positive edge is found with no warnings 5. **Auto-Redeems** - Periodically redeems winnings from resolved markets ## Remixable Template > **This is a customizable template.** The default signal is based on simple price momentum — > you can replace it with: > - Different technical indicators (RSI, MACD, etc.) > - Machine learning model predictions > - External data sources (news, social media sentiment, etc.) > - Custom market scoring algorithms > > The skill handles all the plumbing (market discovery, order execution, position management, safeguards). > Your agent provides the alpha — just replace the `calculate_signal()` function. ## Configuration ### Required Environment Variables - `AION_API_KEY` - Your AION SDK API key (get from https://pm-t1.bxingupdate.com/agents) ### Optional Environment Variables - `WALLET_PRIVATE_KEY` - Only needed for external-wallet self-custody trading - `POLYGON_RPC_URL` - Custom Polygon RPC endpoint (optional) - `MAX_POSITION_SIZE` - Maximum position per market (default: 10 USDC) - `MIN_EDGE_THRESHOLD` - Minimum edge threshold for trading (default: 0.05, i.e., 5%) ## Running Modes ### Dry-run Mode (Default) ```bash python momentum_trader.py ``` This simulates trades without executing real orders. ### Live Trading Mode ```bash python momentum_trader.py --live ``` ⚠️ **Warning**: This executes trades with real funds! ## Customizing Signals Edit the `calculate_signal()` function in `momentum_trader.py` to implement your trading strategy: ```python def calculate_signal(market_data, context): """ Replace this function to implement your custom signal logic Returns: - side: "yes" or "no" or None (no trade) - confidence: confidence level between 0-1 - reasoning: human-readable trade rationale """ # Your custom logic here pass ``` ## Risk Management This skill includes multiple layers of protection: 1. **Flip-flop detection** - Avoids frequent position reversals 2. **Slippage estimation** - Reduces trade size in high-slippage markets 3. **Risk alert checks** - Pauses trading when account-level risks are detected 4. **Position limits** - Maximum exposure per market 5. **Edge validation** - Only trades when expected edge exceeds threshold ## Monitoring & Logs Skill output includes: - Risk status summary - Decision for each market (TRADE, HOLD, SKIP) - Position changes and order updates - Trade execution results ## Example Output ``` ====================================================================== Skill: momentum-polymarket-trader Venue: polymarket (USDC.e) Risk alerts: - none Decisions: - "Will Bitcoin reach $100K in 2025?": HOLD (edge too small +2.1%) - "2026 US Midterm Elections": TRADE YES size=5 reason=momentum signal +7.8% - "OpenAI valuation over $300B?": SKIP (flip-flop warning) Order updates: - cancelled ORDER_123 (stale) - new order ORDER_456 submitted Auto-redemptions: - redeemed market MKT_789: tx=0xabc...def ``` ## Technical Details - **Polling interval**: 15 minutes (with random jitter to avoid synchronization) - **Dependencies**: aion-sdk, requests - **Python version**: 3.8+ - **Network**: Polygon (USDC.e) ## Troubleshooting ### "No opportunity markets found" Check your AION agent settings for risk thresholds and market filters. ### "Invalid API key" Ensure the `AION_API_KEY` environment variable is set correctly. ### "Insufficient balance" Check your wallet balance at https://pm-t1.bxingupdate.com/agents ## Next Steps 1. Install skill: `npx clawhub@latest install momentum-polymarket-trader` 2. Configure your `AION_API_KEY` 3. Test in dry-run mode 4. Customize signal logic 5. Switch to live mode (use caution!) ## License MIT License - free to use, modify, and distribute.

path: README.md
content: # Momentum Polymarket Trader

A complete AION automated trading skill that executes trades on Polymarket based on price momentum signals.

## 📁 File Structure

```
momentum-polymarket-trader/
├── SKILL.md              # AION skill specification and documentation
├── clawhub.json          # ClawHub configuration and automation settings
├── momentum_trader.py    # Main trading logic
├── requirements.txt      # Python dependencies
└── README.md            # This file
```

## 🚀 Quick Start

### 1. Install Dependencies

```bash
pip install -r requirements.txt
```

### 2. Configure Environment Variables

```bash
export AION_API_KEY="your-api-key"  # Get from https://pm-t1.bxingupdate.com/agents
```

Optional configuration:
```bash
export MAX_POSITION_SIZE="10.0"      # Max trade amount per market (USDC)
export MIN_EDGE_THRESHOLD="0.05"     # Minimum trading edge (5%)
```

### 3. Test Run (Dry-run Mode)

```bash
# Single execution
python momentum_trader.py

# Continuous mode
python momentum_trader.py --continuous
```

### 4. Live Trading (⚠️ Use Caution)

```bash
# Single live trade
python momentum_trader.py --live

# Continuous live trading
python momentum_trader.py --live --continuous
```

## 🎯 Features

- ✅ **Auto Market Discovery** - Monitors AION-recommended opportunity markets
- ✅ **Momentum Signal Detection** - Identifies trading opportunities based on price trends
- ✅ **Multi-layer Risk Management** - Flip-flop detection, slippage estimation, risk alerts
- ✅ **Auto-Redemption** - Periodically redeems winnings from resolved markets
- ✅ **Detailed Logging** - Records all decisions and trade reasoning
- ✅ **Customizable Template** - Easy to replace with your own trading signals

## 🔧 Customizing Strategy

Edit the `calculate_signal()` function in `momentum_trader.py`:

```python
def calculate_signal(market: Dict, context: Dict) -> Signal:
    """
    Implement your custom trading strategy here!
    
    You can use:
    - Technical indicators (RSI, MACD, Bollinger Bands, etc.)
    - Machine learning models
    - External data sources (news, social media sentiment, etc.)
    - Any other signal logic
    """
    # Your strategy code...
    pass
```

## 📊 Example Output

```
======================================================================
Skill: momentum-polymarket-trader
Venue: polymarket
Mode: DRY-RUN (simulation)
======================================================================

🔄 Checking for positions to redeem...
✓ Redeemed market abc123...: tx=0xdef456...

📊 Fetching market briefing...
✓ No risk alerts

🎯 Found 5 opportunity markets

Analyzing: Will Bitcoin reach $100K in 2025?...
  • HOLD: No clear momentum signal (YES=0.520, NO=0.480)

Analyzing: 2026 US Midterm Elections...
  💰 TRADE YES: Edge 7.8%
  [DRY-RUN] Simulating trade: YES 6.5 USDC
  Reason: Positive momentum detected for YES (current price=0.680)

======================================================================
Execution Summary
======================================================================
Auto-redeemed: 1 positions
Markets analyzed: 5
Trades executed: 1
Hold decisions: 3
Markets skipped: 1
======================================================================
```

## 📦 Publishing to ClawHub

### 1. Publish Skill

```bash
npx clawhub@latest publish . --slug momentum-polymarket-trader --version 1.0.0
```

### 2. Verify Installation

```bash
npx clawhub@latest install momentum-polymarket-trader
```

### 3. Wait for Sync

The skill will automatically appear in the AION registry within 6 hours: https://pm-t1.bxingupdate.com/agents

### 4. Update Skill

```bash
npx clawhub@latest publish . --slug momentum-polymarket-trader --bump patch
```

## ⚠️ Risk Disclaimer

1. **This is an educational template** - The default momentum strategy is very simple, for demonstration only
2. **Requires customization** - You should implement your own trading logic and signals
3. **Live trading risks** - May result in loss of funds, use with caution
4. **Test thoroughly** - Test extensively in dry-run mode before live trading
5. **Position management** - Set reasonable `MAX_POSITION_SIZE` to limit risk exposure

## 🛠️ Troubleshooting

### Issue: `No opportunity markets found`
**Solution**: Check AION agent settings for market filters and risk thresholds

### Issue: `Invalid API key`
**Solution**: Ensure `AION_API_KEY` environment variable is set correctly

### Issue: `Insufficient balance`
**Solution**: Check wallet balance at https://pm-t1.bxingupdate.com/agents and top up

### Issue: `Failed to import aion_sdk`
**Solution**: Run `pip install aion-sdk`

## 📚 Related Resources

- [AION Documentation](https://docs-t.aionmarket.com/)
- [Building Skills Guide](https://docs-t.aionmarket.com/essentials/building-skills)
- [ClawHub Documentation](https://clawhub.com/docs)
- [Polymarket API](https://docs.polymarket.com/)

## 📝 License

MIT License - free to use, modify, and distribute

## 🤝 Contributing

Issues and pull requests are welcome to improve this skill template!

---

**Note**: This is a customizable template skill. The skill handles all infrastructure (market discovery, order execution, position management, safeguards) - you just provide the trading signal (alpha).


path: clawhub.json
content: {
  "emoji": "📈",
  "primaryEnv": "AION_API_KEY",
  "requires": {
    "pip": ["aion-sdk", "requests"],
    "env": ["AION_API_KEY"]
  },
  "envVars": [
    {
      "name": "AION_API_KEY",
      "required": true,
      "description": "Your AION SDK API key — get from https://pm-t1.bxingupdate.com/agents"
    },
    {
      "name": "WALLET_PRIVATE_KEY",
      "required": false,
      "description": "Only needed for external-wallet self-custody trading. Not required if using AION managed wallets."
    },
    {
      "name": "POLYGON_RPC_URL",
      "required": false,
      "description": "Optional custom Polygon RPC endpoint. Uses default RPC if not set."
    },
    {
      "name": "MAX_POSITION_SIZE",
      "required": false,
      "description": "Maximum position size per market (USDC). Default: 10"
    },
    {
      "name": "MIN_EDGE_THRESHOLD",
      "required": false,
      "description": "Minimum edge threshold required to execute trade (0-1). Default: 0.05 (5%)"
    }
  ],
  "cron": "*/15 * * * *",
  "automaton": {
    "managed": true,
    "entrypoint": "momentum_trader.py"
  }
}


path: momentum_trader.py
content: #!/usr/bin/env python3
"""
Momentum Polymarket Trader Skill

Automated trading on Polymarket based on price momentum signals.
This is a customizable template - replace calculate_signal() to implement your strategy.
"""

import os
import sys
import time
import random
import argparse
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass

try:
    from aion_sdk import AionClient
except ImportError:
    print("Error: aion-sdk not found. Please run: pip install aion-sdk")
    sys.exit(1)


# ============================================================================
# Configuration Constants
# ============================================================================

TRADE_SOURCE = "sdk:momentum-polymarket-trader"
SKILL_SLUG = "momentum-polymarket-trader"
VENUE = "polymarket"

# Read configuration from environment variables
MAX_POSITION_SIZE = float(os.getenv("MAX_POSITION_SIZE", "10.0"))
MIN_EDGE_THRESHOLD = float(os.getenv("MIN_EDGE_THRESHOLD", "0.05"))
BASE_POLL_INTERVAL = 60  # seconds


# ============================================================================
# Client Singleton
# ============================================================================

_client = None

def get_client() -> AionClient:
    """Get AION client singleton"""
    global _client
    if _client is None:
        [REDACTED_SECRET]"AION_API_KEY")
        if not api_key:
            raise ValueError(
                "AION_API_KEY environment variable not found.\n"
                "Get your API key from https://pm-t1.bxingupdate.com/agents and set the environment variable."
            )
        _client = AionClient(
            [REDACTED_SECRET],
            venue=VENUE
        )
    return _client


# ============================================================================
# Data Models
# ============================================================================

@dataclass
class Signal:
    """Trading signal"""
    side: Optional[str]  # "yes", "no", or None
    confidence: float  # 0-1
    reasoning: str
    edge: float = 0.0  # Expected edge


@dataclass
class TradeDecision:
    """Trade decision"""
    market_id: str
    market_question: str
    action: str  # "TRADE", "HOLD", "SKIP"
    side: Optional[str] = None
    size: float = 0.0
    reasoning: str = ""


# ============================================================================
# Core Trading Logic
# ============================================================================

def calculate_signal(market: Dict, context: Dict) -> Signal:
    """
    Calculate trading signal for a market
    
    ⚠️ This is the function you should customize!
    
    Replace this function to implement your trading strategy. You can use:
    - Technical indicators (moving averages, RSI, MACD, etc.)
    - Machine learning models
    - External data sources (news, social media, etc.)
    - Any other signal logic
    
    Args:
        market: Market data dictionary
        context: Market context data (includes trading discipline, slippage estimates, etc.)
    
    Returns:
        Signal object
    """
    # Default implementation: simple price momentum strategy
    # This is just an example - should be replaced with more sophisticated logic
    
    yes_price = market.get("yesPrice", 0.5)
    no_price = market.get("noPrice", 0.5)
    
    # Check if historical price data is available
    # Note: This is a simplified version; real implementation should fetch historical data
    
    # Example logic: if YES price deviates significantly from 0.5, assume momentum
    momentum_threshold = 0.10
    
    if yes_price > 0.5 + momentum_threshold:
        # Price is already high, might continue rising (momentum strategy)
        edge = (yes_price - 0.5) * 0.3  # Simplified edge calculation
        return Signal(
            side="yes",
            confidence=min(0.9, 0.5 + edge),
            reasoning=f"Positive momentum detected for YES (current price={yes_price:.3f})",
            edge=edge
        )
    elif no_price > 0.5 + momentum_threshold:
        # NO price is high
        edge = (no_price - 0.5) * 0.3
        return Signal(
            side="no",
            confidence=min(0.9, 0.5 + edge),
            reasoning=f"Positive momentum detected for NO (current price={no_price:.3f})",
            edge=edge
        )
    else:
        # Price near 0.5, no clear momentum
        return Signal(
            side=None,
            confidence=0.0,
            reasoning=f"No clear momentum signal (YES={yes_price:.3f}, NO={no_price:.3f})",
            edge=0.0
        )


def check_market_context(market_id: str, my_probability: Optional[float] = None) -> Dict:
    """
    Fetch market context and trading discipline data
    
    Context includes:
    - flip-flop warnings
    - slippage estimates
    - edge analysis
    """
    client = get_client()
    params = {}
    if my_probability is not None:
        params["my_probability"] = my_probability
    
    try:
        return client.get_market_context(market_id, **params)
    except Exception as e:
        print(f"Warning: Unable to fetch context for market {market_id}: {e}")
        return {}


def auto_redeem_positions():
    """Auto-redeem winnings from resolved markets"""
    client = get_client()
    results = []
    
    try:
        redemption_results = client.auto_redeem()
        for r in redemption_results:
            if r.get("success"):
                results.append({
                    "market_id": r.get("market_id"),
                    "tx_hash": r.get("tx_hash"),
                    "amount": r.get("amount", 0)
                })
                print(f"✓ Redeemed market {r['market_id'][:20]}...: tx={r['tx_hash'][:10]}...")
        return results
    except Exception as e:
        print(f"Warning: Auto-redemption failed: {e}")
        return []


def execute_trade(
    market: Dict,
    signal: Signal,
    dry_run: bool = True
) -> bool:
    """
    Execute trade
    
    Args:
        market: Market data
        signal: Trading signal
        dry_run: If True, only simulates trade without actual execution
    
    Returns:
        Whether successful
    """
    client = get_client()
    market_id = market.get("id")
    
    # Calculate trade size
    position_size = min(MAX_POSITION_SIZE, 5.0 * signal.confidence)
    
    if dry_run:
        print(f"  [DRY-RUN] Simulating trade: {signal.side.upper()} {position_size:.1f} USDC")
        print(f"  Reason: {signal.reasoning}")
        return True
    
    try:
        result = client.trade(
            market_id=market_id,
            side=signal.side,
            amount=position_size,
            source=TRADE_SOURCE,
            skill_slug=SKILL_SLUG,
            reasoning=signal.reasoning
        )
        
        if result.get("success"):
            print(f"  ✓ Trade executed: {signal.side.upper()} {position_size:.1f} USDC")
            print(f"  Order ID: {result.get('order_id', 'N/A')}")
            return True
        else:
            print(f"  ✗ Trade failed: {result.get('error', 'Unknown error')}")
            return False
            
    except Exception as e:
        print(f"  ✗ Trade execution exception: {e}")
        return False


# ============================================================================
# Main Execution Loop
# ============================================================================

def run_once(dry_run: bool = True) -> Dict:
    """
    Execute one trading cycle
    
    Returns:
        Dictionary containing execution summary
    """
    client = get_client()
    
    print(f"\n{'='*70}")
    print(f"Skill: {SKILL_SLUG}")
    print(f"Venue: {VENUE}")
    print(f"Mode: {'DRY-RUN (simulation)' if dry_run else 'LIVE TRADING ⚠️'}")
    print(f"{'='*70}\n")
    
    # 1. Auto-redeem resolved positions
    print("🔄 Checking for positions to redeem...")
    redeemed = auto_redeem_positions()
    
    # 2. Fetch market briefing
    print("\n📊 Fetching market briefing...")
    try:
        # Note: Wallet address may be needed here; simplified version omits it if API doesn't require
        briefing = client.get_briefing(venue=VENUE, include_markets=True)
    except Exception as e:
        print(f"✗ Unable to fetch briefing: {e}")
        return {"status": "error", "error": str(e)}
    
    # 3. Check risk alerts
    risk_alerts = briefing.get("riskAlerts", [])
    if risk_alerts:
        print(f"\n⚠️  Risk alerts:")
        for alert in risk_alerts:
            print(f"  - {alert}")
        print("\n⏸️  Trading paused due to risk alerts")
        return {"status": "risk-paused", "alerts": risk_alerts}
    else:
        print("✓ No risk alerts")
    
    # 4. Analyze opportunity markets
    opportunity_markets = briefing.get("opportunityMarkets", [])
    print(f"\n🎯 Found {len(opportunity_markets)} opportunity markets")
    
    decisions: List[TradeDecision] = []
    
    for market in opportunity_markets:
        market_id = market.get("id")
        market_question = market.get("question", market.get("title", "Unknown market"))
        
        print(f"\nAnalyzing: {market_question[:60]}...")
        
        # Fetch market context
        context = check_market_context(market_id)
        
        # Check warnings
        warnings = context.get("warnings", [])
        trading_data = context.get("trading", {})
        
        if trading_data.get("flip_flop_warning"):
            print("  ⚠️  Flip-flop detected - skipping")
            decisions.append(TradeDecision(
                market_id=market_id,
                market_question=market_question,
                action="SKIP",
                reasoning="flip-flop warning"
            ))
            continue
        
        if warnings:
            print(f"  ⚠️  Warnings: {', '.join(warnings[:2])} - skipping")
            decisions.append(TradeDecision(
                market_id=market_id,
                market_question=market_question,
                action="SKIP",
                reasoning=f"Context warnings: {warnings[0]}"
            ))
            continue
        
        # Calculate trading signal
        signal = calculate_signal(market, context)
        
        if signal.side is None:
            print(f"  • HOLD: {signal.reasoning}")
            decisions.append(TradeDecision(
                market_id=market_id,
                market_question=market_question,
                action="HOLD",
                reasoning=signal.reasoning
            ))
            continue
        
        # Check if edge is sufficient
        if signal.edge < MIN_EDGE_THRESHOLD:
            print(f"  • HOLD: Edge too small ({signal.edge:.1%} < {MIN_EDGE_THRESHOLD:.1%})")
            decisions.append(TradeDecision(
                market_id=market_id,
                market_question=market_question,
                action="HOLD",
                reasoning=f"Insufficient edge ({signal.edge:.1%})"
            ))
            continue
        
        # Execute trade
        print(f"  💰 TRADE {signal.side.upper()}: Edge {signal.edge:.1%}")
        success = execute_trade(market, signal, dry_run=dry_run)
        
        decisions.append(TradeDecision(
            market_id=market_id,
            market_question=market_question,
            action="TRADE" if success else "FAILED",
            side=signal.side,
            size=min(MAX_POSITION_SIZE, 5.0 * signal.confidence),
            reasoning=signal.reasoning
        ))
    
    # 5. Output summary
    print(f"\n{'='*70}")
    print("Execution Summary")
    print(f"{'='*70}")
    print(f"Auto-redeemed: {len(redeemed)} positions")
    print(f"Markets analyzed: {len(opportunity_markets)}")
    print(f"Trades executed: {sum(1 for d in decisions if d.action == 'TRADE')}")
    print(f"Hold decisions: {sum(1 for d in decisions if d.action == 'HOLD')}")
    print(f"Markets skipped: {sum(1 for d in decisions if d.action == 'SKIP')}")
    print(f"{'='*70}\n")
    
    return {
        "status": "ok",
        "redeemed": len(redeemed),
        "analyzed": len(opportunity_markets),
        "decisions": decisions
    }


def run_continuous(dry_run: bool = True):
    """Continuously run the trading skill (polling with jitter)"""
    print(f"🚀 Starting continuous trading mode...")
    print(f"Base interval: {BASE_POLL_INTERVAL} seconds")
    print(f"Mode: {'DRY-RUN' if dry_run else 'LIVE TRADING'}\n")
    
    iteration = 0
    while True:
        iteration += 1
        print(f"\n>>> Iteration #{iteration}")
        
        try:
            result = run_once(dry_run=dry_run)
            
            if result.get("status") == "error":
                print(f"✗ Error: {result.get('error')}")
                
        except KeyboardInterrupt:
            print("\n\n⏹️  Received interrupt signal, stopping...")
            break
        except Exception as e:
            print(f"\n✗ Unexpected error: {e}")
            import traceback
            traceback.print_exc()
        
        # Random jitter to avoid synchronization
        jitter = random.randint(-8, 8)
        sleep_time = max(15, BASE_POLL_INTERVAL + jitter)
        
        print(f"\n💤 Sleeping for {sleep_time} seconds...")
        time.sleep(sleep_time)


# ============================================================================
# Command Line Interface
# ============================================================================

def main():
    parser = argparse.ArgumentParser(
        description="Momentum Polymarket Trader Skill",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Dry-run single execution (default)
  python momentum_trader.py
  
  # Dry-run continuous mode
  python momentum_trader.py --continuous
  
  # Live trading single execution (⚠️ uses real funds!)
  python momentum_trader.py --live
  
  # Live trading continuous mode
  python momentum_trader.py --live --continuous

Environment variables:
  AION_API_KEY          Required - your AION API key
  MAX_POSITION_SIZE     Optional - max position size (default: 10.0)
  MIN_EDGE_THRESHOLD    Optional - min edge threshold (default: 0.05)
        """
    )
    
    parser.add_argument(
        "--live",
        action="store_true",
        help="Live trading mode (⚠️ uses real funds!)"
    )
    
    parser.add_argument(
        "--continuous",
        action="store_true",
        help="Continuous running mode (regular polling)"
    )
    
    args = parser.parse_args()
    
    # Safety check
    if args.live:
        print("\n" + "="*70)
        print("⚠️  WARNING: Live Trading Mode")
        print("="*70)
        print("You are about to trade with real funds!")
        print("Please ensure you have:")
        print("  1. Tested this skill in dry-run mode")
        print("  2. Understand the trading logic and risks")
        print("  3. Set appropriate position limits")
        print("="*70)
        
        response = input("\nConfirm to continue? Type 'YES' to proceed: ")
        if response != "YES":
            print("Cancelled.")
            return
    
    # Run
    dry_run = not args.live
    
    if args.continuous:
        run_continuous(dry_run=dry_run)
    else:
        run_once(dry_run=dry_run)


if __name__ == "__main__":
    main()


path: requirements.txt
content: aion-sdk>=0.1.0
requests>=2.28.0


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