Replace 10s polling loop with RTDS WebSocket event-driven fill detection
(~100ms latency). Parallelize YES/NO side checks and cancel operations.
Polling kept as 30s safety net fallback.
- Add mmWsFillWatcher.js: subscribes to RTDS activity feed, filters by
own proxy wallet, emits 'fill' events for watched tokens
- Refactor monitorAndManage(): event-driven loop with parallel checks
- Extract checkSideFill() for parallel-safe per-side fill detection
- Wire up WS watcher lifecycle in mm.js and mm-bot.js
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bug 1 (partial fill stuck): When an order is partially filled (e.g. 4/20
shares), the bot would get stuck — isOrderFilled returns false, but the
order is dead on the book. Now detect partial fills via getPartialFillInfo,
cancel the old order, and either re-place a limit for the remaining shares
or market-sell if below CLOB minimum.
Bug 2 (defensive overrides fill): When defensive timeout fires, it cancels
both orders — but one side may have actually filled between the last poll
and the cancel. Now re-check on-chain balances after cancellation. If one
side is filled, route to adaptive CL instead of defensive pivot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace undefined `breakevenFloor` variable with `minAdaptivePrice`
which is the actual breakeven floor calculated from
mmAdaptiveMinCombined - filledLegPrice.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Was using breakevenFloor (1.00 - filledPrice) which ignored the user's
MM_ADAPTIVE_MIN_COMBINED setting. This caused sells below the configured
minimum (e.g. selling at 28c when min combined was 1.01).
Now: filled @ 70c, MM_ADAPTIVE_MIN_COMBINED=1.01 → floor starts at 31c
Phase 1 (>180s): 31c
Phase 2 (90-180s): 21c
Phase 3 (30-90s): 11c
Phase 4 (<30s): market sell
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CLOB API (client.getOrder) can be stale — order filled on-chain but API
still shows unfilled. This caused the bot to miss fills and incorrectly
enter defensive mode or keep monitoring dead orders.
Now isOrderFilled does:
1. Check CLOB API (existing) with retry
2. Fallback: check on-chain token balance via CTF contract
If balance < 5% of original shares → treat as filled
Fixes issue where Polymarket UI shows filled but bot doesn't detect it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Orders must be cancelled BEFORE the monitoring loop can detect fills,
otherwise the limit sell at 70c can still get filled at minute 3
and trigger adaptive CL instead of defensive pivot.
Now: timeout reached → cancel both orders → enter defensivePivot
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check mid price first, place limit at market price (capped at mmSellPrice).
Only falls back to breakeven floor if mid price is below it.
Example: YES filled @ 70c, NO mid = 38c → sell @ 38c (not 30c breakeven)
This gives profit potential instead of always targeting breakeven.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adaptive CL now uses progressive floor lowering for 5-minute markets:
Phase 1 (>180s left): breakeven floor (e.g. 40c for 60c fill)
Phase 2 (90-180s): floor - 10c (accept small loss to escape)
Phase 3 (30-90s): floor - 20c + emergency cut if price < 10c
Phase 4 (<30s): force market sell
Key improvements:
- Standing order: immediately place limit at breakeven floor so brief
bounces get caught (don't wait for polling to detect price >= floor)
- Emergency cut: market sell immediately if price < 10c in phase 3
(market is decisive, bounce unlikely)
- Non-5m markets still use the existing fixed floor (mmAdaptiveMinCombined)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Market start = endTime - 5 minutes, so defensive triggers at exactly
120s after the market opens regardless of when the bot entered.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When neither limit sell fills within MM_DEFENSIVE_TIMEOUT (default 120s),
the bot enters defensive mode:
- Cancel both limit sells
- Wait until 30s before market close
- If worst side price < MM_DEFENSIVE_WORST_THRESHOLD (default 10c):
market sell worst side, hold best side for resolution (best ≈ 90c+)
- If worst side ≥ threshold: merge back to USDC (safe $0 P&L)
Only active for 5-minute markets (MM_DURATION=5m).
New config:
MM_DEFENSIVE_ENABLED (default true)
MM_DEFENSIVE_TIMEOUT (default 120s)
MM_DEFENSIVE_WORST_THRESHOLD (default 0.10)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- isOrderFilled: retry 2x with logging instead of silent error swallow
- Place YES/NO limit sells in parallel via Promise.all
- Market sell immediately when remaining shares < CLOB minimum (5 shares)
- Re-check on-chain balance before every limit/market sell in adaptive CL
- Separate redeemer Safe queue priority from strategy (split/merge) to reduce cross-market delays
- Cache USDC and exchange approvals in-memory to skip redundant on-chain reads
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When one leg fills, instead of immediately market-selling the unfilled
leg, enter a continuous monitoring loop that:
- Places a limit sell only when price >= minAdaptivePrice floor
(floor = mmAdaptiveMinCombined - filledLegPrice, default combined 1.20)
- Chases price upward (>2% improvement → re-place limit higher)
- Cancels limit on dip >5% or below floor, then waits for recovery
- Market-sells only as last resort when CL time is reached
New config: MM_ADAPTIVE_CL (toggle), MM_ADAPTIVE_MIN_COMBINED (floor),
MM_ADAPTIVE_MONITOR_SEC (poll interval). Legacy immediate market-sell
path preserved when MM_ADAPTIVE_CL=false.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename project to polymarket-terminal
- Add Market Maker bot (src/mm.js) with on-chain CTF split/merge/redeem via Gnosis Safe
- Add Orderbook Sniper bot (src/sniper.js) with multi-asset GTC low-price orders
- Add WebSocket watcher (src/services/wsWatcher.js) for real-time RTDS trade events
- Add terminal dashboard UI (src/ui/dashboard.js) using blessed
- Add CTF contract helpers (src/services/ctf.js) for splitPosition, mergePositions, redeemPositions
- Add mmDetector, mmExecutor, sniperDetector, sniperExecutor services
- Add simStats utility for dry-run P&L tracking
- Translate all Indonesian-language strings to professional English across all files
- Rewrite README.md in English with full setup guide, configuration reference, and architecture overview
- Rewrite AGENT.MD in English as comprehensive AI agent and developer reference
- Update package.json name, description, scripts, and keywords
Co-Authored-By: direkturcrypto <direkturcrypto.x@mail3.me>