Status: Phase 0 ✅ Complete
Next: Phases 1-3 (core engine → signals → hardening)
✅ Verified Polymarket API endpoints (Data, Gamma, CLOB)
✅ Project structure created
✅ Config system (env-based, typed dataclasses)
✅ Database schema (SQLite with aiosqlite)
✅ HTTP client stub (async, rate-limiting, retry logic)
✅ README with full API reference + setup guide
✅ requirements.txt (all deps)
✅ main.py template (entry point)
Files to implement:
- Class:
WalletScanner(config, db) - Methods:
async discover_wallets()— Fetch top 100-200 traders from leaderboard, score, filter, persistasync rescore_wallets()— Re-evaluate all active wallets monthly- Score formula:
(win_rate*40) + (log(pnl)*20) + (roi*20) + (recency*20)
- Uses:
polymarket/client.py,signals/scorer.py
- Function:
calculate_wallet_score(wallet_stats: dict) -> float - Inputs: win_rate, total_pnl, roi, last_trade_date
- Output: Score 0-100
- Edge cases: Handle missing fields, invalid dates, log(0)
- Class:
TradeMonitor(config, db, telegram_bot) - Methods:
async monitor_wallets()— For each active wallet:- Fetch recent positions from
/v1/user/{address}/positions - Compare against
tradestable to find NEW trades only - For each new trade, call
signals/filter.pyto check viability - If viable, call
telegram/alerts.pyto send alert - Persist trade + mark alert as sent
- Fetch recent positions from
- Uses:
polymarket/client.py,signals/filter.py,telegram/alerts.py
Files to implement:
- Function:
async is_viable_signal(trade: dict, market: dict, wallet: dict) -> tuple[bool, str, str] - Returns:
(is_viable: bool, alert_type: str, reason: str)alert_type: 'standard' or 'high_conviction'reason: Why signal passed/failed
- Checks (ALL must pass):
- Market still open (not resolved)
- Position size ≥
MIN_SIGNAL_POSITION(default $500) - Liquidity ≥
MIN_MARKET_LIQUIDITY(default $10k) - Hours remaining ≥
MIN_HOURS_REMAINING(default 48) - No previous alert for this wallet-market pair
- Direction matches wallet's historical edge in category (optional; skip if data unavailable)
- New position (not update to existing)
- High conviction detection: If 2+ active wallets have alerts on same market → upgrade to
high_conviction - Uses:
db.py,polymarket/client.py
- Class:
TelegramBot(telegram_config) - Methods:
async send_message(channel_id: str, message: str, parse_mode: str = "HTML")async send_alert(alert_type: str, data: dict)— Dispatch to alerts.py
- Function:
format_standard_alert(wallet: dict, trade: dict, market: dict) -> str - Function:
format_high_conviction_alert(wallets: list, market: dict) -> str - Uses templates from README (HTML format for Telegram)
- Returns HTML-formatted message string
- Uses:
telegram/bot.pyto send
Files to implement:
- Function:
start_scheduler(config, wallet_scanner, trade_monitor, telegram_bot) -> APScheduler.Scheduler - Jobs:
- discover_wallets — Every 6h —
wallet_scanner.discover_wallets() - rescore_wallets — Every 24h —
wallet_scanner.rescore_wallets() - poll_trades — Every 5min —
trade_monitor.monitor_wallets() - check_closures — Every 1h — Delete resolved trades from DB
- daily_digest — 08:00 UTC daily — Summarize day's signals
- discover_wallets — Every 6h —
- Error handling: Catch + log each job exception individually (don't crash scheduler)
- Use:
apscheduler.schedulers.asyncio.AsyncIOScheduler
test_scorer.py— Unit tests for scoring formulatest_filter.py— Unit tests for signal filterconftest.py— Fixtures (mock DB, mock API responses)- Run:
pytest tests/ -v
Dockerfile— Python 3.11 base, install deps, runpython -m polytracker.maindocker-compose.yml— For local dev (mounts .env, data volume)
QUICKSTART.md— 5-minute setup guideAPI_ENDPOINTS.md— Full Polymarket endpoint reference (already in README)
- Always use
asyncmethods:db.get_active_wallets(),db.upsert_trade(), etc. - Never block the event loop with sync DB calls
- Use
db.has_alert_for_market()to prevent duplicate alerts
async with PolymarketClient(config.polymarket) as client:
leaderboard = await client.get_trader_leaderboard(limit=100)
positions = await client.get_wallet_positions(address)
market = await client.get_market_by_slug(slug)- Use
loguruconsistently:logger.info(),logger.error(), etc. - Log all alerts sent with wallet, market, size, score
- Log API errors without crashing (already handled in client.py)
- Wrap all Telegram sends in try/except (log, don't raise)
- All DB operations must be wrapped (transaction safety)
- API failures should be logged but not stop the scheduler
- Use
pytest-asynciofor async tests - Mock Polymarket API responses (fixture in conftest.py)
- Mock Telegram sends (don't actually send during tests)
- Test scoring formula with edge cases (log of zero, missing fields)
import math
def calculate_wallet_score(stats: dict) -> float:
"""
score = (win_rate * 40) + (log(total_pnl) * 20) + (roi * 20) + (recency_bonus * 20)
"""
win_rate = stats.get('win_rate', 0)
total_pnl = stats.get('total_pnl', 1) # Avoid log(0)
roi = stats.get('roi', 0)
last_trade_days_ago = stats.get('last_trade_days_ago', 999)
# Recency bonus
if last_trade_days_ago < 7:
recency = 1.0
elif last_trade_days_ago < 30:
recency = 0.5
else:
recency = 0.0
# Calculate components
win_score = win_rate * 40
pnl_score = max(0, math.log(max(1, total_pnl)) * 20)
roi_score = roi * 20
recency_score = recency * 20
total = win_score + pnl_score + roi_score + recency_score
return min(100, max(0, total)) # Clamp 0-100📡 POLYTRACKER SIGNAL
Wallet: 0x1234...abcd [Score: 87/100]
Win Rate: 73% | PnL: $24,300 | ROI: +142%
📌 Market: "Will X happen by Dec 31?"
Position: YES @ 34¢
Size: $1,200
Liquidity: $85,400
Closes: 14d 6h
🔗 Market: https://polymarket.com/event/will-x-happen
🔗 Wallet: https://polymarket.com/profile/0x1234...abcd
Risk: MEDIUM | Confidence: HIGH
🔥 HIGH CONVICTION — POLYTRACKER
2 tracked whales entered the same market!
Wallets:
• 0x1234...abcd (Score: 87) → YES @ 34¢ ($1,200)
• 0x5678...efgh (Score: 91) → YES @ 36¢ ($3,400)
📌 "Will X happen by Dec 31?"
Combined Position: $4,600
Liquidity: $85,400
Closes: 14d 6h
🔗 https://polymarket.com/event/will-x-happen
- Scorer handles edge cases (log(0), missing fields)
- Filter rejects positions < $500 size
- Filter detects market closures correctly
- Dedup logic prevents duplicate alerts
- Scheduler runs all 5 jobs without crashing
- Telegram sends format correctly (no HTML injection)
- Database transactions don't deadlock
- Rate limiter doesn't exceed 10 req/sec
- Logging captures all alerts to file
- ✅ All source files in
polytracker/(8 new files) - ✅ Test suite with 70%+ coverage
- ✅
.env.example→.envsetup guide - ✅ Docker + docker-compose
- ✅ Run with:
python -m polytracker.main
- Is the wallet filtering correct? Review MIN_* thresholds against leaderboard data
- Can alerts be duplicated? Check
has_alert_for_market()is called before sending - Will the bot survive an API outage? All HTTP errors are caught, logged, not fatal
- Is the database thread-safe? Using aiosqlite (async), not multi-threaded
- Can Telegram failures crash the bot? All bot sends wrapped in try/except
Good luck! You've got this. 🚀