One‑liner: watches Polymarket, detects big moves, and blasts alerts to Discord. 🚨

From config/config.yaml (single source of truth):
- Redis: OFF
- Discord: ON (needs
DISCORD_WEBHOOK_URLin.env) - Dashboard (TUI): ON
- Stdout sink: OFF (keeps the TUI clean)
- Logs: write to
logs/pme.log(console quiet) .envis auto‑loaded on startup (soDISCORD_WEBHOOK_URLworks)
Override order: config/config.yaml → .env → PME__... env vars.
# Edit config/config.yaml as needed
cp config/.env.example .env # put DISCORD_WEBHOOK_URL here
make bootstrap
make runmake runmake run-dashboardPrimary config: config/config.yaml
Secrets: .env (git‑ignored)
Temporary override: PME__... env vars
List‑type envs accept CSV (no JSON needed), e.g. PME__APP__CATEGORIES=finance,politics.
filters.top_k_per_category=0 means no limit (monitor as many as possible).
rolling.enabled=false means don’t collapse by topic (keeps more markets).
gamma.events_limit_per_category=100 caps events per category after full fetch + active filter + ranking (volume → liquidity). More API calls, smaller WS payload. 🧯
filters.focus_keywords=trump,iran,strike focuses monitoring to matching keywords (case‑insensitive). 🎯
gamma.events_sort_primary/secondary control event ranking (default: volume24hr → liquidity). ⚡
signals.major_change_low_price_max=0.05 sets low‑price zone upper bound (e.g. 5¢). 🧊
signals.major_change_low_price_abs=0.01 sets absolute move required in low‑price zone (e.g. 1¢). 🪓
signals.major_change_spread_gate_k=1.5 gates moves smaller than k * spread (kills bounce noise). 🛑
signals.high_confidence_threshold=0.90 drops “high‑confidence” big trades (max(price,1-price) >= threshold). 🧯
signals.reverse_allow_threshold=0.25 always allows reverse‑side big trades (price <= threshold). 🛡️
signals.drop_expired_markets=true drops expired markets from monitoring + signals (end_ts). 🧹
signals.merge_window_sec=60 merges trade signals in a 60s window (split‑order noise killer). 🧷
Example:
PME__DASHBOARD__ENABLED=true \
PME__SINKS__DISCORD__ENABLED=true \
PME__SINKS__REDIS__ENABLED=false \
make run- Live view of monitored markets + prices.
- Multi‑outcome markets are grouped into one row (marked “多选盘”).
- Markets without orderbook show gray as “🚫 无 orderbook”.
- Sort is configurable via
dashboard.sort_by(activity/vol_1m/last_trade/updated/category/title).
Enable (if you turned it off):
PME__DASHBOARD__ENABLED=true make run- Uses Incoming Webhook:
DISCORD_WEBHOOK_URL. - Multi‑outcome alerts are aggregated per market to avoid spam.
- Adjustable:
sinks.discord.aggregate_multi_outcomesinks.discord.aggregate_window_secsinks.discord.aggregate_max_itemssinks.discord.log_payloads+sinks.discord.log_payloads_path(logs all outgoing payloads). 🧷📄
- On startup, Discord receives a “connected + monitored markets” status message.
- Lifecycle/new/removed alerts are not sent to Discord (log only). 🧹
- Health checks are not sent to Discord by default (noise‑free).
- Category counts are event-based (closer to website numbers) and stats include events/markets/tokens.
PME__TOP__ENABLED=true make runOptional:
PME__TOP__LIMITPME__TOP__ORDER(defaultvolume24hr)PME__TOP__FEATURED_ONLY(closest to website Top)
Default: logs go to logs/pme-{ts}.log (per-run timestamped file) and the console stays quiet.
Want console logs back?
PME__LOGGING__CONSOLE=true make runTip: logging.file_path supports {ts} (format: YYYYMMDD-HHMMSS). ✨
On exit, logs emit component_exit with exit_at (local HH:MM:SS). 🧾
make build
make lint
make test
make run
make run-dashboard
make diagnoseBuild a fixed test dataset under tests/fixtures/alert_dataset.jsonl from the Discord log:
python scripts/build_alert_dataset.py --input logs/discord.out.jsonl --output tests/fixtures/alert_dataset.jsonlAdd mock records by appending JSONL rows (set expected_alert to validate rules).
make diagnose- No API key required for public Gamma/CLOB endpoints.
enableOrderBook=falsemarkets are displayed but not subscribed; they still trigger refresh‑based volume alerts (web_volume_spike).- WS 发包会按
clob.max_frame_bytes自动分包;如果还爆1009 message too big,把clob.max_message_bytes调大或关clob.initial_dump。🧱 - Uses
uvloopwhen available for faster async. - Gamma rate limiting is handled by
aiolimiter. - Config merge uses
deepmerge(lists override instead of append). - Tag cache uses
cachetoolsTTL cache. - Discord category stats use
pandasfor concise grouping.
src/
polymarket_monitor_engine/
application/
component.py
monitor.py
signals/
detector.py
STRATEGY_LOG.md
domain/
events.py
models.py
schemas/
event_payloads.py