Add official Polymarket API support, one-click setup, and redesigned README
- Add TRADE_API_MODE setting to switch between official (public, no auth) and internal API - Implement _fetch_trades_official() using Polymarket data-api /trades endpoint - Default to official API so users can run without private API access - Add interactive setup.sh that guides users through API key configuration - Add Makefile with common commands (setup, run, dashboard, etc.) - Add Dockerfile and .dockerignore for container deployment - Redesign README with badges, feature tables, mermaid diagram, architecture - Clean up .env.example with organized sections and signup links - Update pyproject.toml dependencies to match requirements.txt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
venv/
|
||||
.env
|
||||
.git/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
data/
|
||||
reports/
|
||||
daily_briefings/
|
||||
leading_signals/
|
||||
price_volatility/
|
||||
anomaly_signals/
|
||||
*.db
|
||||
*.json.bak
|
||||
+55
-28
@@ -1,35 +1,62 @@
|
||||
# LLM API (Gemini official)
|
||||
GEMINI_API_KEY=your_api_key_here
|
||||
# ============================================================
|
||||
# Polymarket Whale Watcher - Configuration
|
||||
# ============================================================
|
||||
# Copy to .env and fill in your API keys:
|
||||
# cp .env.example .env
|
||||
#
|
||||
# Only GEMINI_API_KEY is required. Everything else is optional.
|
||||
# ============================================================
|
||||
|
||||
# --- Required ---
|
||||
GEMINI_API_KEY=your_gemini_api_key_here
|
||||
|
||||
# --- Trade Data Source ---
|
||||
# "official" = Polymarket public API (no auth needed, default)
|
||||
# "internal" = Private API (requires INTERNAL_API_URL + INTERNAL_API_KEY)
|
||||
TRADE_API_MODE=official
|
||||
|
||||
# Internal API (only needed when TRADE_API_MODE=internal)
|
||||
# INTERNAL_API_URL=http://103.197.25.170:18088
|
||||
# INTERNAL_API_KEY=your_internal_api_key_here
|
||||
|
||||
# --- LLM Settings ---
|
||||
LLM_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai/
|
||||
|
||||
# Internal trade data API
|
||||
INTERNAL_API_URL=http://103.197.25.170:18088
|
||||
INTERNAL_API_KEY=your_internal_api_key_here
|
||||
|
||||
# Polygon Wallet Private Key (for trade execution)
|
||||
POLYGON_WALLET_PRIVATE_KEY=your_private_key_here
|
||||
|
||||
# MongoDB Connection String
|
||||
MONGODB_URI=mongodb://localhost:27017/whale_watcher
|
||||
|
||||
# Whale Detection Settings
|
||||
MIN_TRADE_SIZE_USD=1000
|
||||
MIN_PRICE=0
|
||||
MAX_PRICE=0.7
|
||||
|
||||
# Monitoring Settings
|
||||
FETCH_INTERVAL_SECONDS=5
|
||||
TRENDING_MARKETS_LIMIT=50
|
||||
|
||||
# LLM Settings
|
||||
LLM_MODEL=gemini-3-flash-preview
|
||||
LLM_TEMPERATURE=0
|
||||
|
||||
# Trade Execution (set to true to enable actual trading)
|
||||
# --- Optional: Search & Data APIs (enhances LLM analysis) ---
|
||||
# TAVILY_API_KEY= # Web search (https://tavily.com)
|
||||
# TWITTER_API_KEY= # Twitter sentiment
|
||||
# SERPER_API_KEY= # Web search fallback (https://serper.dev)
|
||||
# POLYGON_API_KEY= # Stocks, forex (https://polygon.io)
|
||||
# FRED_API_KEY= # Economic indicators (https://fred.stlouisfed.org)
|
||||
# ETHERSCAN_API_KEY= # On-chain data (https://etherscan.io)
|
||||
# CONGRESS_API_KEY= # US legislation (https://api.congress.gov)
|
||||
|
||||
# --- Optional: Telegram Monitoring ---
|
||||
# TELEGRAM_API_ID=
|
||||
# TELEGRAM_API_HASH=
|
||||
# TELEGRAM_SESSION_STRING=
|
||||
# TELEGRAM_CHANNELS=
|
||||
|
||||
# --- Whale Detection Tuning ---
|
||||
MIN_TRADE_SIZE_USD=1000
|
||||
MIN_PRICE=0
|
||||
MAX_PRICE=0.7
|
||||
FETCH_INTERVAL_SECONDS=15
|
||||
TRENDING_MARKETS_LIMIT=50
|
||||
|
||||
# --- Email Alerts (optional) ---
|
||||
EMAIL_ENABLED=false
|
||||
# EMAIL_SMTP_SERVER=smtp.qq.com
|
||||
# EMAIL_SMTP_PORT=465
|
||||
# EMAIL_SENDER=
|
||||
# EMAIL_PASSWORD=
|
||||
# EMAIL_RECIPIENT=
|
||||
|
||||
# --- Trade Execution (disabled by default, use with caution) ---
|
||||
ENABLE_TRADE_EXECUTION=false
|
||||
# POLYGON_WALLET_PRIVATE_KEY=
|
||||
|
||||
# Tavily API Key for web search
|
||||
TAVILY_API_KEY=your_tavily_api_key_here
|
||||
|
||||
# Logging
|
||||
# --- Logging ---
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies first (cached layer)
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY src/ src/
|
||||
COPY pyproject.toml .
|
||||
|
||||
# Create data directories
|
||||
RUN mkdir -p data reports daily_briefings
|
||||
|
||||
# Default command
|
||||
CMD ["python", "-m", "src.main", "run"]
|
||||
@@ -0,0 +1,43 @@
|
||||
.PHONY: setup run dashboard briefing markets test lint clean docker-build docker-run help
|
||||
|
||||
PYTHON = python3
|
||||
VENV = venv
|
||||
PIP = $(VENV)/bin/pip
|
||||
PY = $(VENV)/bin/python
|
||||
|
||||
help: ## Show this help
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
setup: ## One-click setup (venv + dependencies + .env)
|
||||
@chmod +x setup.sh && ./setup.sh
|
||||
|
||||
run: ## Start the whale watcher
|
||||
$(PY) -m src.main run
|
||||
|
||||
run-debug: ## Start with debug logging
|
||||
$(PY) -m src.main run --debug
|
||||
|
||||
dashboard: ## Start the web dashboard
|
||||
$(PY) -m src.main dashboard
|
||||
|
||||
briefing: ## Generate today's briefing
|
||||
$(PY) -m src.main briefing --today
|
||||
|
||||
markets: ## Show trending markets
|
||||
$(PY) -m src.main check-markets --limit 20
|
||||
|
||||
test: ## Run tests
|
||||
$(PY) -m pytest tests/ -v
|
||||
|
||||
lint: ## Run linter
|
||||
$(VENV)/bin/ruff check src/
|
||||
|
||||
clean: ## Remove generated files (keep data)
|
||||
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
||||
find . -type f -name '*.pyc' -delete 2>/dev/null || true
|
||||
|
||||
docker-build: ## Build Docker image
|
||||
docker build -t polymarket-whale-watcher .
|
||||
|
||||
docker-run: ## Run in Docker
|
||||
docker run --env-file .env -v $(PWD)/data:/app/data -v $(PWD)/reports:/app/reports polymarket-whale-watcher
|
||||
@@ -1,13 +1,40 @@
|
||||
<div align="center">
|
||||
|
||||
# Polymarket Whale Watcher
|
||||
|
||||
AI-powered whale trade surveillance and analysis system for Polymarket prediction markets. Combines real-time monitoring, multi-dimensional anomaly detection, LLM-driven investigation with 14 autonomous tools, and signal accuracy tracking.
|
||||
**AI-powered whale trade surveillance for Polymarket prediction markets**
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](LICENSE)
|
||||
[](https://docs.polymarket.com/)
|
||||
|
||||
Real-time monitoring | Multi-dimensional anomaly detection | LLM investigation with 14 autonomous tools | Signal accuracy tracking
|
||||
|
||||
[Quick Start](#-quick-start) | [How It Works](#-how-it-works) | [Features](#-features) | [Configuration](#-configuration) | [Dashboard](#-dashboard)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## What It Does
|
||||
|
||||
Whale Watcher continuously monitors 50+ trending Polymarket markets, detects large trades with anomalous patterns, and uses an LLM agent with 14 autonomous research tools to investigate whether the trader may possess an information advantage. It tracks signal accuracy over time, achieving **63.5% win rate** with **+28.3% average ROI** on high-confidence signals.
|
||||
|
||||
```
|
||||
Trending Markets → Whale Detection → Anomaly Scoring → LLM Investigation → Signal Tracking
|
||||
(50+) ($1k-$100k+) (5 dimensions) (14 tools, 5 rounds) (win rate, ROI)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Demo
|
||||
|
||||
<details>
|
||||
<summary><b>Terminal Output</b> — Real-time whale detection and LLM analysis</summary>
|
||||
<details open>
|
||||
<summary><b>Terminal Output</b> — Real-time whale detection and analysis</summary>
|
||||
|
||||
```
|
||||
$ python -m src.main run
|
||||
|
||||
╭──────────────────────────────────────────────────────────╮
|
||||
│ 🐋 Polymarket Whale Watcher │
|
||||
│ │
|
||||
@@ -39,25 +66,25 @@ AI-powered whale trade surveillance and analysis system for Polymarket predictio
|
||||
→ "Over 9M committed to P2P" resolved YES — Signal CORRECT (ROI: +67%)
|
||||
```
|
||||
|
||||
See full example: [docs/examples/sample_terminal.txt](docs/examples/sample_terminal.txt)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Analysis Report</b> — LLM investigation with tool-use</summary>
|
||||
<summary><b>Analysis Report</b> — LLM investigation with autonomous tool-use</summary>
|
||||
|
||||
Each whale trade generates a detailed markdown report:
|
||||
|
||||
- **Trade details** — amount, direction, price, trader wallet
|
||||
- **Trader profile** — rank, PnL, history, recent trades
|
||||
- **LLM investigation** — 5 autonomous tool calls (web, Twitter, Telegram, on-chain, DeFi)
|
||||
- **Trader profile** — leaderboard rank, PnL, history, recent trades
|
||||
- **LLM investigation** — autonomous tool calls (web, Twitter, Telegram, on-chain, DeFi)
|
||||
- **Information asymmetry assessment** — score, evidence, reasoning
|
||||
|
||||
Example findings:
|
||||
Example finding:
|
||||
> *"New ERC-20 contract deployed by MegaETH deployer wallet 6 hours before trade — not yet publicly announced. KOL tweets about insider knowledge preceded the trade by ~3 hours."*
|
||||
>
|
||||
> **Information Asymmetry Score: 0.72** | Trader Credibility: HIGH
|
||||
|
||||
See full example: [docs/examples/sample_report.md](docs/examples/sample_report.md)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@@ -68,7 +95,6 @@ Daily briefings include:
|
||||
- Price volatility alerts
|
||||
- Historical signal performance (win rate, ROI by confidence tier)
|
||||
|
||||
Example stats:
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Win Rate | **63.5%** |
|
||||
@@ -76,193 +102,294 @@ Example stats:
|
||||
| Signals with IAS >= 60% | 3 today |
|
||||
|
||||
See full example: [docs/examples/sample_briefing.md](docs/examples/sample_briefing.md)
|
||||
|
||||
</details>
|
||||
|
||||
## Features
|
||||
|
||||
- **Real-Time Monitoring** — Parallel per-market polling of 50+ trending markets
|
||||
- **Multi-Dimensional Anomaly Detection** — Scores trades on size, price uncertainty, time-of-day, trader deviation, and cluster signals
|
||||
- **Trader Profiling** — Leaderboard ranking, trading history, recent behavior analysis
|
||||
- **LLM Analysis with Tool-Use** — 14 autonomous tools (Twitter, web search, Telegram, crypto prices, stocks, economic data, Congress bills, DeFi metrics, on-chain analysis)
|
||||
- **Signal Accuracy Tracking** — Automatic market resolution checking, win rate stats by confidence tier
|
||||
- **Daily Intelligence Briefing** — Automated 10:00 AM daily summary with high-confidence signals
|
||||
- **Email Alerts** — Real-time notifications for high-IAS signals (>= 60%)
|
||||
- **Web Dashboard** — FastAPI-based signal performance dashboard
|
||||
- **Leading Signal Research** — "Price leads news" dataset collection
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### One-Click Setup
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Configure environment
|
||||
cp .env.example .env
|
||||
# Edit .env with your API keys
|
||||
|
||||
# Start the whale watcher
|
||||
python -m src.main run
|
||||
|
||||
# With debug logging
|
||||
python -m src.main run --debug
|
||||
git clone https://github.com/chaoleiyv/polymarket-whale-watcher.git
|
||||
cd polymarket-whale-watcher
|
||||
chmod +x setup.sh && ./setup.sh
|
||||
```
|
||||
|
||||
The setup script will:
|
||||
1. Check Python 3.10+ is installed
|
||||
2. Create a virtual environment
|
||||
3. Install all dependencies
|
||||
4. Create `.env` from template
|
||||
|
||||
Then add your API key and start:
|
||||
|
||||
```bash
|
||||
# Add your Gemini API key (the only required key)
|
||||
echo "GEMINI_API_KEY=your_key_here" >> .env
|
||||
|
||||
# Activate the environment and run
|
||||
source venv/bin/activate
|
||||
python -m src.main run
|
||||
```
|
||||
|
||||
> **Get a free Gemini API key**: https://aistudio.google.com/apikey
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker build -t whale-watcher .
|
||||
docker run --env-file .env -v ./data:/app/data -v ./reports:/app/reports whale-watcher
|
||||
```
|
||||
|
||||
### Make Commands
|
||||
|
||||
```bash
|
||||
make setup # One-click setup
|
||||
make run # Start monitoring
|
||||
make run-debug # Start with debug logging
|
||||
make dashboard # Start web dashboard
|
||||
make markets # View trending markets
|
||||
make briefing # Generate today's briefing
|
||||
make help # Show all commands
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Real-Time Monitoring** | Parallel per-market polling of 50+ trending markets |
|
||||
| **Anomaly Detection** | 5-dimensional scoring: size, price uncertainty, time-of-day, trader deviation, cluster signals |
|
||||
| **Trader Profiling** | Leaderboard ranking, PnL, trading history, recent behavior |
|
||||
| **LLM Analysis** | 14 autonomous tools across 5 rounds of investigation |
|
||||
| **Signal Tracking** | Automatic market resolution checking, win rate stats by confidence tier |
|
||||
| **Daily Briefing** | Automated 10:00 AM summary with high-confidence signals |
|
||||
| **Email Alerts** | Real-time notifications for high information-asymmetry signals (>= 60%) |
|
||||
| **Web Dashboard** | FastAPI-based signal performance dashboard |
|
||||
| **Official API** | Works with Polymarket's public API — no private API access needed |
|
||||
|
||||
### 14 LLM Research Tools
|
||||
|
||||
The LLM agent autonomously selects and uses these tools during investigation:
|
||||
|
||||
| Category | Tools |
|
||||
|----------|-------|
|
||||
| **Social Sentiment** | `search_twitter`, `search_telegram`, `search_web` |
|
||||
| **Crypto Data** | `get_crypto_price`, `get_defi_metrics`, `get_token_unlocks` |
|
||||
| **Financial Data** | `get_stock_price`, `get_economic_indicators` |
|
||||
| **On-Chain** | `get_wallet_transactions`, `get_contract_info` |
|
||||
| **Legislation** | `search_congress` |
|
||||
| **Market Data** | `get_market_history`, `get_trader_positions` |
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Polymarket API] --> B[Market Fetcher]
|
||||
B --> C[50+ Trending Markets]
|
||||
C --> D[Trade Monitor]
|
||||
D --> E{Whale Trade?}
|
||||
E -->|No| D
|
||||
E -->|Yes| F[Anomaly Detector]
|
||||
F --> G{Score > Threshold?}
|
||||
G -->|No| D
|
||||
G -->|Yes| H[LLM Analyzer]
|
||||
H --> I[14 Research Tools]
|
||||
I --> J[Signal + Report]
|
||||
J --> K[Resolution Tracker]
|
||||
K --> L[Stats Dashboard]
|
||||
```
|
||||
|
||||
### Pipeline
|
||||
|
||||
1. **Market Selection** — Fetches top trending markets by 24h volume from Polymarket, filters out sports/weather/short-term price markets, refreshes every 15 minutes
|
||||
|
||||
2. **Trade Monitoring** — Runs parallel async tasks per market, polls for new trades incrementally, deduplicates by transaction hash
|
||||
|
||||
3. **Anomaly Detection** — Multi-dimensional scoring on 5 axes:
|
||||
- **Size** — Trade size relative to market 24h volume
|
||||
- **Price uncertainty** — Closer to 0.5 = more interesting
|
||||
- **Time-of-day** — ET hour-based suspicion weights
|
||||
- **Trader deviation** — Trade size vs trader's historical average
|
||||
- **Cluster signal** — Same-direction trades within 5-minute window
|
||||
|
||||
4. **LLM Investigation** — Builds rich context (trade + trader profile + market data), LLM autonomously uses tools to investigate (up to 5 rounds), produces structured recommendation with information asymmetry score
|
||||
|
||||
5. **Signal Tracking** — Resolution tracker checks every 30 minutes for resolved markets, validates signal correctness, computes theoretical ROI, aggregates win rates by confidence tier
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy `.env.example` to `.env` and configure:
|
||||
|
||||
**Required:**
|
||||
- `GEMINI_API_KEY` — Gemini API key for LLM analysis
|
||||
- `INTERNAL_API_URL` / `INTERNAL_API_KEY` — Trade data API (currently using internal API, can be replaced with [Polymarket CLOB API](https://docs.polymarket.com/))
|
||||
### Required
|
||||
|
||||
**Optional:**
|
||||
- `TAVILY_API_KEY` — Web search (primary)
|
||||
- `TWITTER_API_KEY` — Twitter sentiment search
|
||||
- `POLYGON_API_KEY` — Stock/ETF data
|
||||
- `FRED_API_KEY` — Economic indicators
|
||||
- `ETHERSCAN_API_KEY` — On-chain data
|
||||
- `EMAIL_*` — Email alert settings
|
||||
- `MIN_TRADE_SIZE_USD` — Minimum trade size (default: 1000)
|
||||
- `MIN_PRICE` / `MAX_PRICE` — Price range filter (default: 0-0.7)
|
||||
| Variable | Description | Get It |
|
||||
|----------|-------------|--------|
|
||||
| `GEMINI_API_KEY` | Gemini API key for LLM analysis | [Google AI Studio](https://aistudio.google.com/apikey) |
|
||||
|
||||
### Trade Data Source
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `TRADE_API_MODE` | `official` | `official` = Polymarket public API (no auth needed), `internal` = private API |
|
||||
|
||||
The bot works out of the box with Polymarket's official public API. No private API access required.
|
||||
|
||||
### Optional (enhances analysis)
|
||||
|
||||
| Variable | Description | Get It |
|
||||
|----------|-------------|--------|
|
||||
| `TAVILY_API_KEY` | Web search (primary) | [tavily.com](https://tavily.com) |
|
||||
| `TWITTER_API_KEY` | Twitter sentiment search | [Twitter Developer](https://developer.twitter.com) |
|
||||
| `POLYGON_API_KEY` | Stock/ETF/forex data | [polygon.io](https://polygon.io) |
|
||||
| `FRED_API_KEY` | Economic indicators | [FRED](https://fred.stlouisfed.org/docs/api/api_key.html) |
|
||||
| `ETHERSCAN_API_KEY` | On-chain wallet analysis | [etherscan.io](https://etherscan.io/apis) |
|
||||
| `CONGRESS_API_KEY` | US legislation data | [congress.gov](https://api.congress.gov/) |
|
||||
|
||||
### Whale Detection Tuning
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `MIN_TRADE_SIZE_USD` | `1000` | Minimum trade size to consider |
|
||||
| `MIN_PRICE` / `MAX_PRICE` | `0` / `0.7` | Price range filter |
|
||||
| `FETCH_INTERVAL_SECONDS` | `15` | Polling interval per market |
|
||||
| `TRENDING_MARKETS_LIMIT` | `50` | Number of markets to monitor |
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Start monitoring
|
||||
python -m src.main run [--debug]
|
||||
# Core
|
||||
python -m src.main run [--debug] # Start monitoring
|
||||
python -m src.main check-markets --limit 20 # View trending markets
|
||||
python -m src.main test-analyze <market_id> # Test LLM on a specific market
|
||||
|
||||
# Check trending markets
|
||||
python -m src.main check-markets --limit 20
|
||||
# Reports
|
||||
python -m src.main briefing --today # Generate today's briefing
|
||||
python -m src.main briefing --date 2026-04-17 # Briefing for a specific date
|
||||
|
||||
# Test LLM analysis on a specific market
|
||||
python -m src.main test-analyze <market_id>
|
||||
# Dashboard
|
||||
python -m src.main dashboard --port 8000 # Start web dashboard
|
||||
|
||||
# Generate daily briefing
|
||||
python -m src.main briefing --today
|
||||
python -m src.main briefing --date 2026-04-17
|
||||
|
||||
# Migrate legacy JSON signals to SQLite
|
||||
python -m src.main migrate
|
||||
|
||||
# Start web dashboard
|
||||
python -m src.main dashboard --port 8000
|
||||
# Maintenance
|
||||
python -m src.main migrate # Migrate legacy JSON to SQLite
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dashboard
|
||||
|
||||
Start the web dashboard to view signal performance:
|
||||
|
||||
```bash
|
||||
python -m src.main dashboard
|
||||
# Open http://localhost:8000
|
||||
```
|
||||
|
||||
The dashboard shows:
|
||||
- Overall signal statistics (total signals, win rate, avg ROI)
|
||||
- Performance breakdown by confidence tier
|
||||
- Top best/worst signals by theoretical ROI
|
||||
- Paginated signal history
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── config/settings.py # Environment configuration (Pydantic)
|
||||
├── models/ # Data models
|
||||
│ ├── market.py # Market, TrendingMarket
|
||||
│ ├── trade.py # TradeActivity, WhaleTrade, TraderRanking
|
||||
│ ├── decision.py # TradeRecommendation, LLMDecision
|
||||
│ └── anomaly_signal.py # AnomalySignal (stored signal)
|
||||
├── services/ # Business logic (23 modules)
|
||||
│ ├── market_fetcher.py # Polymarket Gamma API
|
||||
│ ├── trade_monitor.py # Per-market parallel monitoring (official + internal API)
|
||||
│ ├── anomaly_detector.py # Multi-dimensional anomaly scoring
|
||||
│ ├── llm_analyzer.py # LLM with tool-use (14 tools, 5 rounds)
|
||||
│ ├── tools.py # Tool registry
|
||||
│ ├── resolution_tracker.py # Market resolution checking
|
||||
│ ├── stats_engine.py # Performance statistics
|
||||
│ ├── daily_briefing.py # Daily summary generation
|
||||
│ └── [data services] # Twitter, Telegram, CoinGecko, DeFiLlama,
|
||||
│ # FRED, Polygon, Etherscan, Congress, web search
|
||||
├── db/database.py # SQLite signal storage
|
||||
├── prompts/ # LLM system prompts & tool schemas
|
||||
├── dashboard.py # FastAPI web dashboard
|
||||
└── main.py # CLI entry point (Typer)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Polymarket API Internal Trade API Gamma API
|
||||
| | |
|
||||
v v v
|
||||
MarketFetcher TradeMonitor PriceMonitor
|
||||
| | |
|
||||
v v v
|
||||
TrendingMarkets AnomalyDetector VolatilityAnalyzer
|
||||
|
|
||||
v
|
||||
LLMAnalyzer (14 tools)
|
||||
| |
|
||||
v v
|
||||
AnomalySignal Reports/Alerts
|
||||
|
|
||||
v
|
||||
ResolutionTracker → StatsEngine → Dashboard
|
||||
┌─────────────────────────────┐
|
||||
│ Polymarket Gamma API │
|
||||
│ (trending markets, prices) │
|
||||
└──────────────┬──────────────┘
|
||||
│
|
||||
┌──────────────▼──────────────┐
|
||||
│ Market Fetcher │
|
||||
│ (filter sports/weather/etc) │
|
||||
└──────────────┬──────────────┘
|
||||
│
|
||||
┌────────────────────▼────────────────────┐
|
||||
│ Trade Monitor (async) │
|
||||
│ 50+ parallel market polling tasks │
|
||||
│ Official API ←→ Internal API (switch) │
|
||||
└────────────────────┬────────────────────┘
|
||||
│
|
||||
┌────────────────────▼────────────────────┐
|
||||
│ Anomaly Detector │
|
||||
│ 5-axis scoring: size, price, time, │
|
||||
│ trader deviation, cluster signals │
|
||||
└────────────────────┬────────────────────┘
|
||||
│
|
||||
┌────────────────────▼────────────────────┐
|
||||
│ LLM Analyzer (Gemini) │
|
||||
│ 14 tools × 5 rounds of investigation │
|
||||
├─────────┬────────┬────────┬─────────────┤
|
||||
│ Twitter │ Web │ DeFi │ On-Chain │
|
||||
│Telegram │ Search │ Crypto │ Congress │
|
||||
└─────────┴───┬────┴────────┴─────────────┘
|
||||
│
|
||||
┌─────────────▼──────────────────────────┐
|
||||
│ Signal Storage (SQLite) │
|
||||
│ → Resolution Tracker (every 30min) │
|
||||
│ → Stats Engine (win rate, ROI) │
|
||||
│ → Dashboard (FastAPI) │
|
||||
│ → Email Alerts (IAS >= 60%) │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── config/settings.py # Environment configuration
|
||||
├── models/
|
||||
│ ├── market.py # Market, TrendingMarket
|
||||
│ ├── trade.py # TradeActivity, WhaleTrade, TraderRanking
|
||||
│ ├── decision.py # TradeRecommendation, LLMDecision
|
||||
│ ├── anomaly_signal.py # AnomalySignal (stored signal)
|
||||
│ └── leading_signal.py # LeadingSignal (price leads news)
|
||||
├── services/
|
||||
│ ├── market_fetcher.py # Polymarket API, market filtering
|
||||
│ ├── trade_monitor.py # Per-market parallel monitoring
|
||||
│ ├── price_monitor.py # Volatility detection
|
||||
│ ├── anomaly_detector.py # Multi-dimensional anomaly scoring
|
||||
│ ├── llm_analyzer.py # LLM with tool-use (14 tools, 5 rounds max)
|
||||
│ ├── volatility_analyzer.py # Leading signal detection
|
||||
│ ├── trader_profiler.py # Trader profile generation
|
||||
│ ├── tools.py # Tool registry
|
||||
│ ├── daily_briefing.py # Daily summary generation
|
||||
│ ├── resolution_tracker.py # Market resolution checking
|
||||
│ ├── stats_engine.py # Performance statistics
|
||||
│ ├── anomaly_history.py # Signal storage (SQLite)
|
||||
│ ├── coingecko.py # Crypto prices
|
||||
│ ├── fred.py # Economic indicators (FRED)
|
||||
│ ├── polygon.py # Stock prices & news
|
||||
│ ├── congress.py # US legislation
|
||||
│ ├── defillama.py # DeFi TVL, revenue, token unlocks
|
||||
│ ├── etherscan.py # On-chain wallet analysis
|
||||
│ ├── twitter_search.py # Twitter API
|
||||
│ ├── telegram_search.py # Telegram channels
|
||||
│ └── web_search.py # Unified search (Tavily → Serper → DDG)
|
||||
├── db/database.py # SQLite signal storage
|
||||
├── prompts/
|
||||
│ ├── whale_analyzer.py # LLM system prompt & tool schemas
|
||||
│ └── volatility_analyzer.py # Volatility analysis prompt
|
||||
├── dashboard.py # FastAPI web dashboard
|
||||
└── main.py # Entry point (WhaleWatcher orchestrator)
|
||||
|
||||
data/ # SQLite database + processed transactions
|
||||
reports/ # Analysis reports (by date)
|
||||
daily_briefings/ # Daily intelligence summaries
|
||||
leading_signals/ # "Price leads news" research dataset
|
||||
price_volatility/ # Volatility alert records
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. Market Selection
|
||||
- Fetches top trending markets by 24h volume from Polymarket Gamma API
|
||||
- Filters out sports, weather, and short-term price markets
|
||||
- Refreshes market list every 15 minutes
|
||||
|
||||
### 2. Trade Monitoring
|
||||
- Runs parallel async tasks per monitored market
|
||||
- Polls internal API incrementally (new trades since last check)
|
||||
- Rate-limited at 5 QPS to respect API limits
|
||||
- Deduplicates by transaction hash
|
||||
|
||||
### 3. Anomaly Detection
|
||||
Multi-dimensional scoring on 5 axes:
|
||||
- **Size** — Trade size relative to market 24h volume
|
||||
- **Price uncertainty** — Closer to 0.5 = more interesting
|
||||
- **Time-of-day** — ET hour-based suspicion weights
|
||||
- **Trader deviation** — Trade size vs trader's historical average
|
||||
- **Cluster signal** — Same-direction trades within 5-minute window
|
||||
|
||||
### 4. LLM Investigation
|
||||
When a whale trade triggers:
|
||||
1. Builds rich context: trade details + trader profile + market data + historical signals
|
||||
2. LLM autonomously uses tools to investigate (up to 5 rounds):
|
||||
- Search Twitter/Telegram for insider chatter
|
||||
- Check crypto prices, DeFi metrics, on-chain activity
|
||||
- Look up stock movements, economic data, Congress bills
|
||||
- Web search for breaking news
|
||||
3. Produces structured recommendation: action, confidence, information asymmetry score (0-1)
|
||||
4. Generates markdown report saved to `reports/`
|
||||
|
||||
### 5. Signal Tracking
|
||||
- Resolution tracker checks every 30 minutes for resolved markets
|
||||
- Validates signal correctness against actual outcomes
|
||||
- Computes theoretical ROI for each signal
|
||||
- Stats engine aggregates win rates by confidence tier
|
||||
---
|
||||
|
||||
## Safety
|
||||
|
||||
- Trade execution disabled by default (`ENABLE_TRADE_EXECUTION=false`)
|
||||
- Trade execution **disabled by default** (`ENABLE_TRADE_EXECUTION=false`)
|
||||
- Position size capped at 20% of balance if enabled
|
||||
- Minimum 60% confidence threshold for execution
|
||||
- Price range filter avoids obvious outcomes (0-0.7)
|
||||
- Price range filter avoids obvious outcomes
|
||||
- All decisions logged for audit trail
|
||||
- Rate limiting on all external APIs
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This system is for research and educational purposes. Prediction market trading involves significant risk. Never trade with funds you cannot afford to lose. Always verify recommendations independently.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
+5
-9
@@ -9,22 +9,18 @@ description = "AI-powered whale trade detection and analysis bot for Polymarket"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
license = {text = "MIT"}
|
||||
authors = [
|
||||
{name = "Your Name", email = "your@email.com"}
|
||||
]
|
||||
dependencies = [
|
||||
"httpx>=0.27.0",
|
||||
"pydantic>=2.0.0",
|
||||
"pydantic-settings>=2.0.0",
|
||||
"python-dotenv>=1.0.0",
|
||||
"openai>=1.0.0",
|
||||
"langchain>=0.1.0",
|
||||
"langchain-openai>=0.0.5",
|
||||
"pymongo>=4.6.0",
|
||||
"motor>=3.3.0",
|
||||
"py-clob-client>=0.17.0",
|
||||
"web3>=6.0.0",
|
||||
"rich>=13.0.0",
|
||||
"typer>=0.9.0",
|
||||
"duckduckgo-search>=7.0.0",
|
||||
"telethon>=1.36.0",
|
||||
"fastapi>=0.100.0",
|
||||
"uvicorn>=0.20.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env bash
|
||||
# Polymarket Whale Watcher - One-Click Setup
|
||||
# Usage: chmod +x setup.sh && ./setup.sh
|
||||
|
||||
set -e
|
||||
|
||||
CYAN='\033[0;36m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
DIM='\033[2m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${CYAN}"
|
||||
echo "╭──────────────────────────────────────────────────────────╮"
|
||||
echo "│ 🐋 Polymarket Whale Watcher Setup │"
|
||||
echo "╰──────────────────────────────────────────────────────────╯"
|
||||
echo -e "${NC}"
|
||||
|
||||
# ============================================================
|
||||
# Step 1: Check Python
|
||||
# ============================================================
|
||||
echo -e "${CYAN}[1/4]${NC} Checking Python version..."
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo -e "${RED}Error: Python 3 is required but not installed.${NC}"
|
||||
echo " Install from https://www.python.org/downloads/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
|
||||
PYTHON_MAJOR=$(echo "$PYTHON_VERSION" | cut -d. -f1)
|
||||
PYTHON_MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f2)
|
||||
|
||||
if [ "$PYTHON_MAJOR" -lt 3 ] || ([ "$PYTHON_MAJOR" -eq 3 ] && [ "$PYTHON_MINOR" -lt 10 ]); then
|
||||
echo -e "${RED}Error: Python 3.10+ required, found $PYTHON_VERSION${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e " ${GREEN}✓${NC} Python $PYTHON_VERSION"
|
||||
|
||||
# ============================================================
|
||||
# Step 2: Virtual environment
|
||||
# ============================================================
|
||||
echo -e "${CYAN}[2/4]${NC} Setting up virtual environment..."
|
||||
if [ ! -d "venv" ]; then
|
||||
python3 -m venv venv
|
||||
echo -e " ${GREEN}✓${NC} Virtual environment created"
|
||||
else
|
||||
echo -e " ${GREEN}✓${NC} Virtual environment already exists"
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# Step 3: Install dependencies
|
||||
# ============================================================
|
||||
echo -e "${CYAN}[3/4]${NC} Installing dependencies..."
|
||||
source venv/bin/activate
|
||||
pip install --upgrade pip -q
|
||||
pip install -r requirements.txt -q
|
||||
echo -e " ${GREEN}✓${NC} Dependencies installed"
|
||||
|
||||
# Create data directories
|
||||
mkdir -p data reports daily_briefings
|
||||
|
||||
# ============================================================
|
||||
# Step 4: API Key Configuration
|
||||
# ============================================================
|
||||
echo -e "${CYAN}[4/4]${NC} Configuring API keys..."
|
||||
echo ""
|
||||
|
||||
if [ -f ".env" ]; then
|
||||
echo -e " ${YELLOW}Found existing .env file.${NC}"
|
||||
read -p " Overwrite and reconfigure? [y/N] " overwrite
|
||||
if [[ ! "$overwrite" =~ ^[Yy]$ ]]; then
|
||||
echo -e " ${GREEN}✓${NC} Keeping existing .env"
|
||||
echo ""
|
||||
echo -e "${GREEN}Setup complete! Run:${NC}"
|
||||
echo " source venv/bin/activate"
|
||||
echo " python -m src.main run"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
cp .env.example .env
|
||||
|
||||
# Helper function: prompt for a key and write to .env
|
||||
set_key() {
|
||||
local var_name="$1"
|
||||
local prompt_text="$2"
|
||||
local url="$3"
|
||||
local required="$4"
|
||||
|
||||
echo ""
|
||||
if [ "$required" = "required" ]; then
|
||||
echo -e " ${BOLD}${var_name}${NC} ${RED}(required)${NC}"
|
||||
else
|
||||
echo -e " ${BOLD}${var_name}${NC} ${DIM}(optional, press Enter to skip)${NC}"
|
||||
fi
|
||||
echo -e " ${DIM}$prompt_text${NC}"
|
||||
echo -e " ${DIM}→ $url${NC}"
|
||||
|
||||
while true; do
|
||||
read -p " Key: " key_value
|
||||
if [ -z "$key_value" ]; then
|
||||
if [ "$required" = "required" ]; then
|
||||
echo -e " ${RED}This key is required. Please enter a value.${NC}"
|
||||
continue
|
||||
else
|
||||
echo -e " ${DIM}Skipped${NC}"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
break
|
||||
done
|
||||
|
||||
# Replace the placeholder in .env
|
||||
if grep -q "^${var_name}=" .env; then
|
||||
sed -i.bak "s|^${var_name}=.*|${var_name}=${key_value}|" .env
|
||||
elif grep -q "^# ${var_name}=" .env; then
|
||||
sed -i.bak "s|^# ${var_name}=.*|${var_name}=${key_value}|" .env
|
||||
else
|
||||
echo "${var_name}=${key_value}" >> .env
|
||||
fi
|
||||
rm -f .env.bak
|
||||
echo -e " ${GREEN}✓ Saved${NC}"
|
||||
}
|
||||
|
||||
echo -e "${CYAN}╭──────────────────────────────────────────────────────────╮${NC}"
|
||||
echo -e "${CYAN}│ API Key Configuration │${NC}"
|
||||
echo -e "${CYAN}│ │${NC}"
|
||||
echo -e "${CYAN}│ The more keys you add, the better the analysis. │${NC}"
|
||||
echo -e "${CYAN}│ Only Gemini is required. Others are optional but │${NC}"
|
||||
echo -e "${CYAN}│ strongly recommended for full coverage. │${NC}"
|
||||
echo -e "${CYAN}╰──────────────────────────────────────────────────────────╯${NC}"
|
||||
|
||||
# --- Required ---
|
||||
echo ""
|
||||
echo -e "${YELLOW}━━━ Required (LLM Engine) ━━━${NC}"
|
||||
|
||||
set_key "GEMINI_API_KEY" \
|
||||
"Powers the LLM analysis (free tier available)" \
|
||||
"https://aistudio.google.com/apikey" \
|
||||
"required"
|
||||
|
||||
# --- Core Search ---
|
||||
echo ""
|
||||
echo -e "${YELLOW}━━━ Web Search (strongly recommended) ━━━${NC}"
|
||||
echo -e "${DIM} Without these, LLM falls back to DuckDuckGo (lower quality)${NC}"
|
||||
|
||||
set_key "TAVILY_API_KEY" \
|
||||
"Best web search quality, 1000 free searches/month" \
|
||||
"https://app.tavily.com/home"
|
||||
|
||||
set_key "SERPER_API_KEY" \
|
||||
"Google search fallback, 2500 free searches" \
|
||||
"https://serper.dev"
|
||||
|
||||
# --- Social Sentiment ---
|
||||
echo ""
|
||||
echo -e "${YELLOW}━━━ Social Sentiment ━━━${NC}"
|
||||
|
||||
set_key "TWITTER_API_KEY" \
|
||||
"Twitter/X sentiment & breaking news search" \
|
||||
"https://developer.x.com/en/portal/dashboard"
|
||||
|
||||
# --- Financial Data ---
|
||||
echo ""
|
||||
echo -e "${YELLOW}━━━ Financial & On-Chain Data ━━━${NC}"
|
||||
|
||||
set_key "POLYGON_API_KEY" \
|
||||
"Stock, ETF, forex, commodities data (free tier)" \
|
||||
"https://polygon.io/dashboard/signup"
|
||||
|
||||
set_key "FRED_API_KEY" \
|
||||
"US economic indicators (free, instant approval)" \
|
||||
"https://fred.stlouisfed.org/docs/api/api_key.html"
|
||||
|
||||
set_key "ETHERSCAN_API_KEY" \
|
||||
"On-chain wallet & contract analysis (free tier)" \
|
||||
"https://etherscan.io/myapikey"
|
||||
|
||||
set_key "CONGRESS_API_KEY" \
|
||||
"US legislation & bills tracking (free)" \
|
||||
"https://api.congress.gov/sign-up/"
|
||||
|
||||
# --- Summary ---
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Count configured keys
|
||||
configured=0
|
||||
total=8
|
||||
for var in GEMINI_API_KEY TAVILY_API_KEY SERPER_API_KEY TWITTER_API_KEY POLYGON_API_KEY FRED_API_KEY ETHERSCAN_API_KEY CONGRESS_API_KEY; do
|
||||
val=$(grep "^${var}=" .env 2>/dev/null | cut -d= -f2-)
|
||||
if [ -n "$val" ] && [ "$val" != "your_gemini_api_key_here" ]; then
|
||||
configured=$((configured + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}╭──────────────────────────────────────────────────────────╮${NC}"
|
||||
echo -e "${GREEN}│ ✅ Setup Complete! │${NC}"
|
||||
echo -e "${GREEN}│ │${NC}"
|
||||
echo -e "${GREEN}│ API Keys configured: ${configured}/${total} │${NC}"
|
||||
echo -e "${GREEN}╰──────────────────────────────────────────────────────────╯${NC}"
|
||||
|
||||
if [ "$configured" -lt 4 ]; then
|
||||
echo ""
|
||||
echo -e " ${YELLOW}Tip: More API keys = better analysis coverage.${NC}"
|
||||
echo -e " ${YELLOW}You can edit .env anytime to add more keys later.${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Start monitoring:"
|
||||
echo -e " ${BOLD}source venv/bin/activate${NC}"
|
||||
echo -e " ${BOLD}python -m src.main run${NC}"
|
||||
echo ""
|
||||
echo " Other commands:"
|
||||
echo " python -m src.main check-markets # View trending markets"
|
||||
echo " python -m src.main dashboard # Web dashboard"
|
||||
echo " python -m src.main briefing --today # Daily briefing"
|
||||
echo ""
|
||||
@@ -15,7 +15,10 @@ class Settings(BaseSettings):
|
||||
gemini_api_key: str = Field(default="", alias="GEMINI_API_KEY")
|
||||
llm_base_url: str = Field(default="http://apicz.boyuerichdata.com/v1/", alias="LLM_BASE_URL")
|
||||
|
||||
# Internal trade data API
|
||||
# Trade data API mode: "internal" (private API) or "official" (Polymarket data-api)
|
||||
trade_api_mode: str = Field(default="official", alias="TRADE_API_MODE")
|
||||
|
||||
# Internal trade data API (only used when TRADE_API_MODE=internal)
|
||||
internal_api_url: str = Field(default="http://103.197.25.170:18088", alias="INTERNAL_API_URL")
|
||||
internal_api_key: str = Field(default="", alias="INTERNAL_API_KEY")
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ class TradeMonitor:
|
||||
logger.info(f"Now monitoring {len(self._monitored_markets)} markets")
|
||||
|
||||
# ================================================================
|
||||
# Internal API: fetch trades
|
||||
# Trade fetching: dispatches to internal or official API
|
||||
# ================================================================
|
||||
|
||||
_MAX_RETRIES = 3
|
||||
@@ -166,7 +166,171 @@ class TradeMonitor:
|
||||
|
||||
async def fetch_market_trades(self, market_id: str) -> List[TradeActivity]:
|
||||
"""
|
||||
Fetch recent taker trades for a market using the /flows API.
|
||||
Fetch recent trades for a market. Dispatches to internal or official API
|
||||
based on TRADE_API_MODE setting.
|
||||
"""
|
||||
if self.settings.trade_api_mode == "internal":
|
||||
return await self._fetch_trades_internal(market_id)
|
||||
else:
|
||||
return await self._fetch_trades_official(market_id)
|
||||
|
||||
# ================================================================
|
||||
# Official Polymarket data-api: fetch trades
|
||||
# ================================================================
|
||||
|
||||
async def _fetch_trades_official(self, market_id: str) -> List[TradeActivity]:
|
||||
"""
|
||||
Fetch recent trades using the official Polymarket data-api /trades endpoint.
|
||||
|
||||
The official API returns trades with fields:
|
||||
- id, taker_order_id, market, asset, side, size, price, status
|
||||
- match_time, transaction_hash, outcome, bucket_index, owner, type
|
||||
"""
|
||||
try:
|
||||
market = self._monitored_markets.get(market_id)
|
||||
if not market:
|
||||
return []
|
||||
|
||||
# The official /trades endpoint uses condition_id as the "market" param
|
||||
condition_id = market.condition_id
|
||||
if not condition_id:
|
||||
return []
|
||||
|
||||
last_ts = self._market_last_ts.get(market_id)
|
||||
|
||||
params: Dict[str, object] = {
|
||||
"market": condition_id,
|
||||
"limit": 50 if last_ts is None else 500,
|
||||
}
|
||||
|
||||
# Incremental polling: only fetch trades after last seen timestamp
|
||||
if last_ts is not None:
|
||||
params["after"] = last_ts + 1
|
||||
|
||||
sem = self._api_sem or asyncio.Semaphore(20)
|
||||
last_err: Optional[Exception] = None
|
||||
async with sem:
|
||||
for attempt in range(self._MAX_RETRIES):
|
||||
try:
|
||||
async with self._api_lock:
|
||||
now = _time.monotonic()
|
||||
wait = self._api_global_interval - (now - self._api_last_request)
|
||||
if wait > 0:
|
||||
await asyncio.sleep(wait)
|
||||
self._api_last_request = _time.monotonic()
|
||||
|
||||
response = await self._client.get(
|
||||
f"{self.data_api_url}/trades", params=params,
|
||||
)
|
||||
response.raise_for_status()
|
||||
break
|
||||
except httpx.HTTPStatusError as e:
|
||||
if e.response.status_code in (502, 503, 504) and attempt < self._MAX_RETRIES - 1:
|
||||
delay = self._RETRY_BACKOFF[attempt]
|
||||
logger.debug(
|
||||
f"Official API {e.response.status_code} for {market_id} "
|
||||
f"(attempt {attempt + 1}/{self._MAX_RETRIES}), "
|
||||
f"retrying in {delay}s"
|
||||
)
|
||||
await asyncio.sleep(delay)
|
||||
continue
|
||||
raise
|
||||
except httpx.HTTPError as e:
|
||||
last_err = e
|
||||
if attempt < self._MAX_RETRIES - 1:
|
||||
delay = self._RETRY_BACKOFF[attempt]
|
||||
logger.debug(
|
||||
f"Official API retry for {market_id} "
|
||||
f"(attempt {attempt + 1}/{self._MAX_RETRIES}): "
|
||||
f"{type(e).__name__}, retrying in {delay}s"
|
||||
)
|
||||
await asyncio.sleep(delay)
|
||||
else:
|
||||
logger.warning(
|
||||
f"Official API connection error for {market_id} "
|
||||
f"(attempt {attempt + 1}/{self._MAX_RETRIES}, giving up): "
|
||||
f"{type(e).__name__}: {e}"
|
||||
)
|
||||
return []
|
||||
else:
|
||||
return []
|
||||
|
||||
data = response.json()
|
||||
if not data:
|
||||
return []
|
||||
|
||||
activities = []
|
||||
max_ts = last_ts or 0
|
||||
|
||||
for item in data:
|
||||
try:
|
||||
side = item.get("side", "").upper()
|
||||
|
||||
# Only track BUY trades (new positions)
|
||||
if side != "BUY":
|
||||
continue
|
||||
|
||||
size = float(item.get("size", 0) or 0)
|
||||
price = float(item.get("price", 0) or 0)
|
||||
usdc_size = size * price # Official API: USDC value = tokens * price
|
||||
|
||||
outcome = item.get("outcome", "Yes")
|
||||
outcome_index = int(item.get("outcomeIndex", 0 if outcome == "Yes" else 1))
|
||||
|
||||
# Timestamp is epoch seconds in the official API
|
||||
ts = int(item.get("timestamp", 0) or 0)
|
||||
if ts == 0:
|
||||
ts = int(_time.time())
|
||||
|
||||
if ts > max_ts:
|
||||
max_ts = ts
|
||||
|
||||
tx_hash = item.get("transactionHash", "")
|
||||
|
||||
activity = TradeActivity(
|
||||
transaction_hash=tx_hash,
|
||||
timestamp=ts,
|
||||
condition_id=item.get("conditionId", condition_id),
|
||||
asset=item.get("asset", ""),
|
||||
side="BUY",
|
||||
size=size,
|
||||
usdc_size=usdc_size,
|
||||
price=price,
|
||||
outcome=outcome,
|
||||
outcome_index=outcome_index,
|
||||
title=item.get("title", ""),
|
||||
slug=item.get("slug"),
|
||||
event_slug=item.get("eventSlug"),
|
||||
proxy_wallet=item.get("proxyWallet"),
|
||||
name=item.get("name") or item.get("pseudonym"),
|
||||
)
|
||||
activities.append(activity)
|
||||
except Exception as e:
|
||||
logger.debug(f"Failed to parse official API trade: {e}")
|
||||
continue
|
||||
|
||||
if max_ts > 0:
|
||||
self._market_last_ts[market_id] = max_ts
|
||||
|
||||
return activities
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.warning(
|
||||
f"Official trades API HTTP {e.response.status_code} for {market_id}: "
|
||||
f"{e.response.text[:200]}"
|
||||
)
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.warning(f"Error fetching official trades for {market_id}: {type(e).__name__}: {e}")
|
||||
return []
|
||||
|
||||
# ================================================================
|
||||
# Internal API: fetch trades
|
||||
# ================================================================
|
||||
|
||||
async def _fetch_trades_internal(self, market_id: str) -> List[TradeActivity]:
|
||||
"""
|
||||
Fetch recent taker trades for a market using the internal /flows API.
|
||||
|
||||
/flows returns one record per taker per transaction (already aggregated
|
||||
across maker fills), with accurate usd_amount and real execution price.
|
||||
|
||||
Reference in New Issue
Block a user