Architectureļ
The service is intentionally built around a Watcher / Action abstraction so that new observable events and new notification channels can be added without touching existing code.
High-Level Designļ
flowchart TD
subgraph startup["Startup"]
CFG[Config\nconfig.yaml] --> SVC[WatcherService]
MR[MarketResolver\nGamma REST API] --> SVC
end
SVC -->|asset_ids| WS[PolymarketWebSocketClient\nwss://ws-subscriptions-clob.polymarket.com/ws/market]
subgraph event_loop["Event Loop"]
WS -->|raw JSON frame| DISPATCH[_dispatch_event\nevent bus / fan-out]
DISPATCH -->|book / price_change| PSW[PriceSupportWatcher]
DISPATCH -.->|future events| FW1[SmartMoneyWatcher\nš® future]
DISPATCH -.->|future events| FW2[MarketDisputeWatcher\nš® future]
DISPATCH -.->|future events| FW3[ExternalPlatformWatcher\nš® future]
end
subgraph actions["Actions"]
PSW -->|alert payload| LA[LogAction\nstdout placeholder]
PSW -.->|alert payload| FA1[SMSAction\nš® future]
PSW -.->|alert payload| FA2[DiscordAction\nš® future]
PSW -.->|alert payload| FA3[TelegramAction\nš® future]
FW1 -.->|alert payload| FA1
FW1 -.->|alert payload| FA2
end
style startup fill:#f0f4ff,stroke:#aac
style event_loop fill:#fff8e8,stroke:#ca9
style actions fill:#f0fff4,stroke:#9c9
Module Mapļ
polymarket_watcher/
āāā __init__.py
āāā config.py ā dataclass-based YAML config loader
āāā market_resolver.py ā slug ā (yes_token_id, no_token_id) via Gamma API
āāā order_book.py ā local OrderBook state, bid_support_within_pct()
āāā websocket_client.py ā auto-reconnecting WebSocket client (websockets lib)
āāā service.py ā orchestrator: wires everything together
āāā main.py ā entry point with signal handling
āāā admin/ ā local admin CLI (SSH-based, run on your machine)
ā āāā admin_config.py ā per-user config (~/.config/polymarket-watcher/admin.yaml)
ā āāā cli.py ā Click-based CLI (init/status/logs/restart/config)
ā āāā editor.py ā cross-platform editor selection ($EDITOR / VS Code / nano)
ā āāā ssh.py ā ssh/scp subprocess helpers
ā āāā tui.py ā streaming log viewer (journalctl -f over SSH)
āāā watchers/
ā āāā base_watcher.py ā abstract BaseWatcher
ā āāā price_support_watcher.py ā detects bid-support drop alerts
āāā actions/
āāā base_action.py ā abstract BaseAction
āāā log_action.py ā placeholder: logs alert payload to stdout
Data Flowļ
Startup ā
WatcherServiceloadsConfig, resolves the market slug to two CLOB token IDs (YES + NO) via the Gamma REST API, then instantiates all configured watchers.Connection ā
PolymarketWebSocketClientopens a persistent WebSocket towss://ws-subscriptions-clob.polymarket.com/ws/marketand sends a subscription frame containing both token IDs.Inbound events ā The API sends two kinds of events:
bookā full order-book snapshot (sent on subscription and after major state changes).price_changeā incremental update to one or more price levels.
Dispatch ā
WatcherService._dispatch_eventfans each event out to every registered watcher. Watchers that declaresupported_event_typesare skipped for irrelevant events (performance optimisation).Watchers ā Each watcher maintains its own internal state (e.g. a local
OrderBookcopy) and fires Actions when its condition is met.Actions ā Each action receives a structured
event_datadict and performs a side-effect (log, SMS, Discord message, etc.).
Ideas for Future Watchersļ
Watcher |
Trigger |
Data source |
|---|---|---|
|
Large single orders above threshold |
|
|
Dispute / resolution event |
Polymarket REST API |
|
Spread or depth crosses threshold |
|
|
Correlated price movement on Kalshi / Metaculus |
External REST polling |
|
24-h volume spike vs rolling average |
Gamma REST API |