22fcb4355b
New scripts: - health_check.py: one-command session-start workflow (portfolio + live prices + drawdown + stop losses → GREEN/YELLOW/RED status) - backtest.py: performance analysis with live-readiness assessment against CLAUDE.md prerequisites (20+ trades, >55% win rate, Sharpe >0.5) - correlation_tracker.py: detects hidden correlated exposure in portfolio (e.g., 3 insider-trading bets = one cluster) - setup_wallet.py: burner wallet creation, env var verification, on-chain balance check for live trading setup Also adds: - .env.example template for live trading configuration - .well-known/skills/index.json for Agent Skills registry discovery - Updated SKILL.md files documenting new scripts - .gitignore entries for .env, .polymarket-live/, and key files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
269 lines
9.1 KiB
Python
269 lines
9.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Polymarket Live Trading — Wallet Setup & Verification
|
|
|
|
Creates a burner wallet OR verifies an existing configuration.
|
|
Run this BEFORE your first live trade.
|
|
|
|
Usage:
|
|
python setup_wallet.py --create # Generate new burner wallet
|
|
python setup_wallet.py --verify # Check existing env vars
|
|
python setup_wallet.py --check-balance # Verify on-chain USDC balance
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import stat
|
|
from pathlib import Path
|
|
|
|
# Polygon USDC contract
|
|
USDC_CONTRACT = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
|
|
POLYGON_RPC = "https://polygon-rpc.com"
|
|
|
|
|
|
def create_wallet():
|
|
"""Generate a new burner wallet and output setup instructions."""
|
|
try:
|
|
from eth_account import Account
|
|
except ImportError:
|
|
print("ERROR: eth_account not installed.")
|
|
print("Install with: pip install eth-account")
|
|
print("")
|
|
print("Alternative: use 'cast wallet new' (Foundry) or MetaMask.")
|
|
sys.exit(1)
|
|
|
|
acct = Account.create()
|
|
address = acct.address
|
|
private_key = acct.key.hex()
|
|
|
|
print("=" * 60)
|
|
print(" NEW BURNER WALLET CREATED")
|
|
print("=" * 60)
|
|
print(f" Address: {address}")
|
|
print(f" Private Key: {private_key}")
|
|
print("=" * 60)
|
|
print("")
|
|
print("SAVE THE PRIVATE KEY NOW — it cannot be recovered.")
|
|
print("")
|
|
print("Next steps:")
|
|
print(f" 1. Fund {address} with USDC on Polygon")
|
|
print(" - Start with $25 or less (First Time tier)")
|
|
print(" - Send ~0.1 MATIC for gas fees")
|
|
print(" 2. Set up your .env file:")
|
|
print("")
|
|
|
|
env_path = Path(__file__).parent.parent / ".env"
|
|
example_path = Path(__file__).parent.parent / ".env.example"
|
|
|
|
if not env_path.exists() and example_path.exists():
|
|
print(f" cp {example_path} {env_path}")
|
|
print(f" chmod 600 {env_path}")
|
|
print(f" # Edit {env_path} and paste your private key")
|
|
else:
|
|
print(f" export POLYMARKET_PRIVATE_KEY={private_key}")
|
|
print(" export POLYMARKET_CONFIRM=true")
|
|
print(" export POLYMARKET_MAX_SIZE=5")
|
|
print(" export POLYMARKET_DAILY_LOSS_LIMIT=10")
|
|
|
|
print("")
|
|
print(" 3. Verify setup:")
|
|
print(" python setup_wallet.py --verify")
|
|
print("")
|
|
print("SECURITY REMINDERS:")
|
|
print(" - This is a BURNER wallet — only fund what you can lose")
|
|
print(" - NEVER use your main wallet's private key")
|
|
print(" - NEVER commit .env to git")
|
|
print(" - NEVER paste the private key into chat")
|
|
|
|
|
|
def verify_config():
|
|
"""Check that all required env vars are set and valid."""
|
|
print("=" * 60)
|
|
print(" LIVE TRADING CONFIGURATION CHECK")
|
|
print("=" * 60)
|
|
print("")
|
|
|
|
checks = []
|
|
|
|
# Check POLYMARKET_PRIVATE_KEY
|
|
pk = os.environ.get("POLYMARKET_PRIVATE_KEY", "")
|
|
if not pk:
|
|
checks.append(("POLYMARKET_PRIVATE_KEY", "MISSING", "Not set"))
|
|
elif not pk.startswith("0x") or len(pk) != 66:
|
|
checks.append(("POLYMARKET_PRIVATE_KEY", "INVALID", "Must be 0x + 64 hex chars"))
|
|
else:
|
|
masked = pk[:6] + "..." + pk[-4:]
|
|
checks.append(("POLYMARKET_PRIVATE_KEY", "OK", f"Set ({masked})"))
|
|
|
|
# Check POLYMARKET_CONFIRM
|
|
confirm = os.environ.get("POLYMARKET_CONFIRM", "")
|
|
if confirm != "true":
|
|
checks.append(("POLYMARKET_CONFIRM", "MISSING", f"Got '{confirm}', need 'true'"))
|
|
else:
|
|
checks.append(("POLYMARKET_CONFIRM", "OK", "Safety gate enabled"))
|
|
|
|
# Check POLYMARKET_MAX_SIZE
|
|
max_size = os.environ.get("POLYMARKET_MAX_SIZE", "")
|
|
if not max_size:
|
|
checks.append(("POLYMARKET_MAX_SIZE", "DEFAULT", "Not set, will use $10 default"))
|
|
else:
|
|
try:
|
|
val = float(max_size)
|
|
tier = "First time" if val <= 5 else "Learning" if val <= 10 else "Experienced" if val <= 50 else "Advanced"
|
|
checks.append(("POLYMARKET_MAX_SIZE", "OK", f"${val:.0f} per trade ({tier} tier)"))
|
|
except ValueError:
|
|
checks.append(("POLYMARKET_MAX_SIZE", "INVALID", f"Got '{max_size}', need a number"))
|
|
|
|
# Check POLYMARKET_DAILY_LOSS_LIMIT
|
|
dll = os.environ.get("POLYMARKET_DAILY_LOSS_LIMIT", "")
|
|
if not dll:
|
|
checks.append(("POLYMARKET_DAILY_LOSS_LIMIT", "DEFAULT", "Not set, will use $50 default"))
|
|
else:
|
|
try:
|
|
val = float(dll)
|
|
checks.append(("POLYMARKET_DAILY_LOSS_LIMIT", "OK", f"${val:.0f} daily limit"))
|
|
except ValueError:
|
|
checks.append(("POLYMARKET_DAILY_LOSS_LIMIT", "INVALID", f"Got '{dll}', need a number"))
|
|
|
|
# Check .env file permissions
|
|
env_path = Path(__file__).parent.parent / ".env"
|
|
if env_path.exists():
|
|
mode = oct(stat.S_IMODE(env_path.stat().st_mode))
|
|
if mode == "0o600":
|
|
checks.append((".env permissions", "OK", f"{mode} (owner read/write only)"))
|
|
else:
|
|
checks.append((".env permissions", "WARN", f"{mode} — should be 0o600. Run: chmod 600 {env_path}"))
|
|
else:
|
|
checks.append((".env file", "INFO", "No .env file found (using shell env vars)"))
|
|
|
|
# Check .gitignore
|
|
gitignore_path = Path(__file__).parent.parent.parent / ".gitignore"
|
|
if gitignore_path.exists():
|
|
content = gitignore_path.read_text()
|
|
if ".env" in content:
|
|
checks.append((".gitignore", "OK", ".env is gitignored"))
|
|
else:
|
|
checks.append((".gitignore", "WARN", ".env NOT in .gitignore — add it!"))
|
|
|
|
# Print results
|
|
all_ok = True
|
|
for name, status, detail in checks:
|
|
icon = {"OK": "+", "MISSING": "X", "INVALID": "X", "WARN": "!", "DEFAULT": "~", "INFO": "-"}
|
|
print(f" [{icon.get(status, '?')}] {name}: {detail}")
|
|
if status in ("MISSING", "INVALID"):
|
|
all_ok = False
|
|
|
|
print("")
|
|
if all_ok:
|
|
print(" STATUS: READY for live trading")
|
|
print("")
|
|
print(" Before your first trade, also run:")
|
|
print(" python setup_wallet.py --check-balance")
|
|
print(" python check_positions.py --balance")
|
|
else:
|
|
print(" STATUS: NOT READY — fix the issues above")
|
|
|
|
return all_ok
|
|
|
|
|
|
def check_balance():
|
|
"""Query on-chain USDC balance for the configured wallet."""
|
|
pk = os.environ.get("POLYMARKET_PRIVATE_KEY", "")
|
|
if not pk or not pk.startswith("0x") or len(pk) != 66:
|
|
print("ERROR: POLYMARKET_PRIVATE_KEY not set or invalid.")
|
|
print("Run: python setup_wallet.py --verify")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
from eth_account import Account
|
|
acct = Account.from_key(pk)
|
|
address = acct.address
|
|
except ImportError:
|
|
print("ERROR: eth_account not installed. pip install eth-account")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
import requests
|
|
except ImportError:
|
|
print("ERROR: requests not installed. pip install requests")
|
|
sys.exit(1)
|
|
|
|
print(f"Checking balance for: {address}")
|
|
print("")
|
|
|
|
# Check MATIC balance
|
|
payload = {
|
|
"jsonrpc": "2.0",
|
|
"method": "eth_getBalance",
|
|
"params": [address, "latest"],
|
|
"id": 1,
|
|
}
|
|
try:
|
|
resp = requests.post(POLYGON_RPC, json=payload, timeout=10)
|
|
result = resp.json().get("result", "0x0")
|
|
matic_wei = int(result, 16)
|
|
matic = matic_wei / 1e18
|
|
matic_status = "OK" if matic >= 0.01 else "LOW — need ~0.1 MATIC for gas"
|
|
print(f" MATIC: {matic:.4f} ({matic_status})")
|
|
except Exception as e:
|
|
print(f" MATIC: ERROR fetching — {e}")
|
|
|
|
# Check USDC balance (ERC20 balanceOf)
|
|
# balanceOf(address) selector = 0x70a08231
|
|
padded_addr = address[2:].lower().zfill(64)
|
|
payload = {
|
|
"jsonrpc": "2.0",
|
|
"method": "eth_call",
|
|
"params": [
|
|
{"to": USDC_CONTRACT, "data": f"0x70a08231{padded_addr}"},
|
|
"latest",
|
|
],
|
|
"id": 2,
|
|
}
|
|
try:
|
|
resp = requests.post(POLYGON_RPC, json=payload, timeout=10)
|
|
result = resp.json().get("result", "0x0")
|
|
usdc_raw = int(result, 16)
|
|
usdc = usdc_raw / 1e6 # USDC has 6 decimals
|
|
tier = "First time" if usdc <= 25 else "Learning" if usdc <= 100 else "Experienced" if usdc <= 500 else "Advanced"
|
|
print(f" USDC: ${usdc:.2f} ({tier} tier)")
|
|
except Exception as e:
|
|
print(f" USDC: ERROR fetching — {e}")
|
|
|
|
print("")
|
|
print("If balance is zero, fund the wallet:")
|
|
print(f" Send USDC (Polygon) to: {address}")
|
|
print(" Send ~0.1 MATIC for gas fees")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Polymarket live trading wallet setup and verification"
|
|
)
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
group.add_argument(
|
|
"--create", action="store_true", help="Generate a new burner wallet"
|
|
)
|
|
group.add_argument(
|
|
"--verify", action="store_true", help="Verify existing env var configuration"
|
|
)
|
|
group.add_argument(
|
|
"--check-balance",
|
|
action="store_true",
|
|
help="Check on-chain USDC and MATIC balance",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if args.create:
|
|
create_wallet()
|
|
elif args.verify:
|
|
verify_config()
|
|
elif args.check_balance:
|
|
check_balance()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|