For project structure, quick start, configuration reference, and deployment see README.md.
make install # Install dependencies (go mod download)
make build # Build binary → bin/polyoracle
make build-linux # Cross-compile for Linux x86_64 → bin/polyoracle-linux-amd64
make test # Run all tests
make test-coverage # Run tests with coverage
make run # Build and run with configs/config.yaml
make fmt # Format code with gofmt
make lint # Run golangci-lint
make dev # Development mode with auto-reload (requires entr)
make docker-build # Build Docker image
make docker-run # Run Docker container
make clean # Remove binaries and data directorySingle binary service with polling architecture:
- Config Loader → Reads YAML from
configs/config.yaml - Monitor Service → Orchestrates polling cycles
- Polymarket Client → Fetches events from Gamma API + CLOB API
- Storage → SQLite-backed persistence via
modernc.org/sqlite(no CGO); WAL mode - Change Detection → Four-factor composite scoring: KL divergence × log-volume weight × historical SNR × trajectory consistency; results ranked via
ScoreAndRank - Telegram Client → Sends notifications for top K changes
Data flow: Poll → Store → Detect Changes → Notify → Persist
cmd/polyoracle/main.go— Entry point, orchestrationconfigs/config.yaml.example— Annotated config template (SSoT for all config fields and defaults)configs/config.test.yaml— Local test overrides (debug logging; same values as example otherwise)internal/config/config.go— Config loading & validation; Go-side defaultsinternal/logger/logger.go— Structured logger (init withlogger.Init(level, format))internal/monitor/monitor.go— Composite scoring and ranking algorithm (ScoreAndRank)
Table-driven tests using the standard Go testing package:
make test # All tests
make test-coverage # With coverage
go test ./internal/monitor -v # Specific packageTests located: internal/**/*_test.go
- Config file required: Service fails without valid
configs/config.yaml; copy fromconfigs/config.yaml.example - Telegram credentials:
telegram.bot_tokenandtelegram.chat_idare required whentelegram.enabled = true - Storage path: Default uses OS tmp dir (
$TMPDIR/polyoracle/data.db); override with envPOLY_ORACLE_STORAGE_DB_PATH - Categories filter: Only monitors events in configured categories; see
docs/valid-categories.mdfor valid slugs - Volume filter OR logic: Events pass if they meet ANY one threshold ($24hr OR $1wk OR $1mo)
- Telegram MarkdownV2: Notification messages use MarkdownV2 format with automatic escaping of special characters
- Category field often null: Polymarket API
categoryfield is frequently null; actual category info is intags[]array — filtering uses tag slugs - Multi-market event tracking: Events with multiple markets are tracked separately. Each market gets a composite ID (
EventID:MarketID), enabling per-market change detection.
Polymarket events can have multiple markets (e.g., "Will Bitcoin hit $X?" with separate markets for different dates). The service tracks each market independently:
- Composite ID:
EventID:MarketIDformat ensures unique tracking - Market-specific changes: Probability changes are detected per market
- Telegram notifications: Show which specific market changed (with market question)
- URL handling: All markets share the same event URL
Example — an event "Will Bitcoin hit price targets?" with 3 markets:
event123:market1→ "Will Bitcoin hit $100K by March?"event123:market2→ "Will Bitcoin hit $150K by June?"event123:market3→ "Will Bitcoin hit $200K by Dec?"
Go 1.24+ (latest stable): follow standard Go conventions.