Commit Graph

34 Commits

Author SHA1 Message Date
direkturcrypto 930dffafbd fix: add exchange approval, sell retry with settlement delay, CL at 10s
Three fixes for live trading:
1. Call ensureExchangeApproval before selling — CLOB buy gives tokens
   but exchange needs ERC1155 operator approval to place sell orders
2. Wait 2s after buy fill for on-chain settlement before placing sell,
   retry up to 3x with increasing delay if balance not yet available
3. CL at 10s before market close — cancel unfilled sell orders and let
   tokens resolve on-chain naturally

Also adds simulation stats tracking:
- Starting balance $50, tracks cumulative P&L
- Win/Loss/Skip counters with win rate %
- Trade history displayed in TUI and bot status

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:58:11 +07:00
direkturcrypto 9a89efb071 fix: remove cut-loss from maker strategy — hold to resolution
No cut-loss needed: worst case is losing buy cost (2c/share) which
resolves on-chain. If on winning side, payout is $1/share.
Removed marketSell, cutLossSells, and makerCutLossTime config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:59:58 +07:00
direkturcrypto 23bb74127c fix: rewrite makerWs to handle CLOB WebSocket message formats correctly
- Handle initial book snapshot (arrives as array without event_type)
- Handle price_change deltas (incremental updates to bid/ask levels)
- Use Maps for O(1) price level updates instead of full array replace
- Use proper WebSocket ping frames instead of string "ping"
- Subscribe all assets in single message
- Add getBestBid/getBestAsk methods using cached values from events
- Improve TUI orderbook display with depth bars and spread indicator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:53:04 +07:00
direkturcrypto 991fd6caa4 feat: implement buy-low-sell-high market maker (no splitPosition)
New market maker strategy that places limit BUY on UP+DOWN at low price
(default 2c), then sells at target price (default 3c) when filled.

Key features:
- Concurrent limit BUY on both sides (multi-thread style via Promise.all)
- Immediate partial sell placement on partial fill detection
- Cancel opposite side when one fills completely
- WebSocket orderbook client for real-time simulation visualization
- Both TUI (maker.js) and bot (maker-bot.js) entry points

Files:
- services/makerDetector.js: market detection (reuses slug pattern)
- services/makerExecutor.js: core buy-low-sell-high strategy
- services/makerWs.js: WebSocket orderbook for live data
- maker.js: TUI version with blessed dashboard
- maker-bot.js: PM2/VPS plain-text version

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:47:26 +07:00
direkturcrypto 9e78fb1bfe fix: update sniper logging for 3-tier config
- Fix sniperDetector.js to use sniperTierPrices instead of old config
- Fix sniper-tui.js display for 3-tier pricing
- Remove references to deleted sniperPrice/sniperShares vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:59:14 +07:00
direkturcrypto c524ed92ef feat: implement 3-tier sniper strategy with weighted sizing
- Replace single price/shares with 3-tier system (3c/2c/1c)
- Tier allocation: 20%/30%/50% (high→low price)
- Min 5 shares per tier enforced
- Update .env.example with new config vars
- Update README.md with strategy documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:52:37 +07:00
direkturcrypto 00235dc850 fix: inject proxy agent into @polymarket/clob-client
Add proxy support directly to clob-client http-helpers:
- Import https-proxy-agent
- Add getProxyAgent() helper
- Inject agent into axios config for polymarket.com endpoints

This ensures all CLOB API calls route through the proxy.
2026-03-01 22:36:23 +07:00
direkturcrypto f5c777ec0c simplify: use HttpsProxyAgent directly for proxy routing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:33:45 +07:00
direkturcrypto 048d4433c2 fix: implement CONNECT tunnel for proxy routing
Full HTTPS CONNECT tunnel implementation that:
- Routes only Polymarket domains through proxy
- Uses HTTP CONNECT method for HTTPS tunneling
- Leaves all other traffic (RPC, etc.) untouched

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:27:18 +07:00
direkturcrypto b770f831db fix: simplify proxy patch to use HttpsProxyAgent globally
Set https.globalAgent and override https.request to use proxy
for all Polymarket domains. Ensures @polymarket/clob-client
axios calls route through proxy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:22:20 +07:00
direkturcrypto 368a3f8da5 fix: ensure proxy is used by @polymarket/clob-client
Add proxy-patch.cjs that patches https.request BEFORE axios is loaded.
This forces all Polymarket API calls through the proxy, including
internal axios instances used by @polymarket/clob-client.

Import patch as very first thing in sniper.js and sniper-tui.js.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:18:06 +07:00
direkturcrypto b2f8447d0b perf: cache sniper losses to speed up redeem checks
- Add _skippedLosses Set to cache confirmed losses (skip future checks)
- Add counters for skipped categories (unresolved, losses, no balance)
- Losses only checked once, subsequent cycles skip immediately
- Add summary logging for better visibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:01:56 +07:00
direkturcrypto c5b169b47e feat: sniper redeem - winners only, high gas, no startup check
- Remove startup redeem check (only runs on interval)
- Only redeem WINNING positions (skip losses for manual cleanup)
- Increase gas fees: 1.5x estimate, min 50 Gwei priority fee
- Add tx hash logging for successful redeems

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:42:26 +07:00
direkturcrypto 374e943cc2 feat: redeem logging improvements
- Add transaction hash to redeem success log
- Continue to next position when one fails (don't get stuck)
- Add clearer logging for retry behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:30:19 +07:00
direkturcrypto d24c5f2e81 feat: add undici dependency + dynamic gas multiplier for tx retry
- Install undici package for fetch proxy support
- Add gas multiplier on retry to handle "replacement transaction underpriced"
  - Attempt 1: 1x gas
  - Attempt 2: 2x gas
  - Attempt 3: 4x gas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:23:37 +07:00
direkturcrypto 56f59eabdf fix: sniper redeem matches MM redeemer — redeem all, skip failed conditions
- Redeem ALL resolved positions (not just winners) — burns loser tokens
  to clean them from Data API, prevents buildup that slows down cycles
- Add _failedConditions set: skip conditionIds that revert on-chain
  so they don't retry every 60 seconds
- Same code path as the working MM redeemer (redeemMMPositions)
- Log win/loss separately for profit tracking
2026-03-01 21:10:12 +07:00
direkturcrypto 4af99a78f5 fix: simplify sniper redeem — remove MultiSend and gas estimation timeout
- Remove MultiSend batching (was causing hangs)
- Revert execSafeCall to original behavior (no gas estimation timeout)
- Keep win-only filter: skip positions with $0 payout
- Simple 1-by-1 redeem identical to the working MM redeemer
- Fix proxy: use undici ProxyAgent with dispatcher for native fetch
- Move schedule to .env: SNIPER_SCHEDULE_BTC=19:40-22:40,03:40-06:10
2026-03-01 20:55:06 +07:00
direkturcrypto 0dc8024ceb fix: proxy uses undici ProxyAgent for fetch, schedule moved to .env, verbose redeem logging
- Fix proxy: native fetch needs undici ProxyAgent with dispatcher option
  (https-proxy-agent only works with axios, not Node's built-in fetch)
- Move schedule from hardcoded to .env: SNIPER_SCHEDULE_BTC=19:40-22:40,03:40-06:10
- Add verbose logging to redeemSniperPositions for debugging
- Config dynamically reads all SNIPER_SCHEDULE_* env vars
2026-03-01 19:07:57 +07:00
direkturcrypto f31f257da5 feat: sniper bot updates — TUI separation, session scheduling, proxy support, win-only redeem
- Separate TUI from sniper: npm run sniper (console), npm run sniper-tui (TUI)
- Add trading session scheduling per asset (BTC/ETH/SOL/XRP) in UTC+8
- Add PROXY_URL support for Polymarket CLOB/Gamma/Data APIs (not Polygon RPC)
- Create redeemSniperPositions: win-only filter, bulk MultiSend batching
- Add gas estimation with 10s timeout in execSafeCall
- Replace native fetch with proxyFetch in all Polymarket API services
2026-03-01 16:24:25 +07:00
direkturcrypto 5ea7f3d2b0 feat: add PM2-compatible MM entry point (src/mm-bot.js)
- src/mm-bot.js: same MM logic as mm.js but no blessed TUI; writes plain
  text to stdout so pm2 logs works on VPS; status printed every 60s
- ecosystem.config.cjs: added polymarket-mm app with separate log files
  (logs/mm-out.log, logs/mm-error.log); both copy and MM bots now managed
- package.json: added mm-bot / mm-bot-sim / mm-bot-dev scripts

Usage:
  pm2 start ecosystem.config.cjs --only polymarket-mm
  pm2 start ecosystem.config.cjs --only polymarket-mm --env sim
  pm2 logs polymarket-mm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 02:05:06 +07:00
direkturcrypto 363d74ff2d Merge branch 'feat/mm-adaptive-cl' 2026-02-26 01:35:04 +07:00
direkturcrypto 0148b0bbef feat: GTC fallback when FAK finds no liquidity (next market copy trades)
When copying a trader who buys into the next periodic market before sellers
exist, FAK returns 0 fill each attempt. After exhausting FAK retries, the
bot now falls back to a GTC limit order at price*1.02 and polls getOrder()
every 3s until filled or GTC_FALLBACK_TIMEOUT (default 60s) expires.

If the GTC times out it is cancelled. GTC_FALLBACK_TIMEOUT=0 disables the
fallback entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 04:30:12 +07:00
direkturcrypto 365dc4e164 fix: serialize concurrent buys per market + always verify on-chain payout before redeem
executeBuy (race condition):
- Multiple WebSocket events for the same market can arrive concurrently.
  All calls saw no existing position and all proceeded to buy → 3x fills.
- Added _buyQueue (Map<conditionId, Promise>) that chains each buy for
  the same market after the previous one. Second call now sees the filled
  position and respects maxPositionSize.

redeemer (gas estimation error):
- Gamma API can return resolved=true before payoutDenominator is written
  on-chain. Calling redeemPositions when payoutDenominator==0 causes the
  contract to revert → UNPREDICTABLE_GAS_LIMIT from ethers.js.
- Now always verify on-chain payout after the API check. If on-chain
  payout not set yet, skip and retry next interval.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 04:22:09 +07:00
direkturcrypto 526fc7a057 feat: add PM2-compatible entry point (src/bot.js) and ecosystem config
- src/bot.js: same logic as index.js but no blessed TUI; writes plain
  text to stdout so pm2 logs works cleanly on VPS
- ecosystem.config.cjs: pm2 config with live/sim envs, log files at
  logs/out.log + logs/error.log, auto-restart policy
- logs/.gitkeep: track the logs/ dir (*.log files are gitignored)
- package.json: added bot / bot-sim / bot-dev scripts

Usage on VPS:
  pm2 start ecosystem.config.cjs            # live
  pm2 start ecosystem.config.cjs --env sim  # simulation
  pm2 logs polymarket-copy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 04:17:16 +07:00
direkturcrypto 0650cb9ef7 fix: correct Gamma API field names in getMarketOptions (all camelCase)
The Gamma API returns camelCase field names but the code used snake_case,
causing all lookups to return undefined:
  - end_date_iso  → endDate  (full ISO datetime, not date-only endDateIso)
  - game_start_time → removed (field doesn't exist)
  - minimum_tick_size → orderPriceMinTickSize
  - neg_risk      → negRisk
  - condition_id  → conditionId
  - accepting_orders → acceptingOrders

Because endDate was always null, the MIN_MARKET_TIME_LEFT expiry guard
was silently skipped on every buy — allowing buys into expired markets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:49:38 +07:00
direkturcrypto 59295b08cb fix: remove duplicate 'client' declaration in executeSell
The const client = getClient() was declared twice in the same function scope
(line 318 for cancel-orders block, line 371 for the sell loop), causing a
SyntaxError at startup. Removed the redundant second declaration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:44:32 +07:00
direkturcrypto b52892172e fix: enforce $1 USDC minimum per FAK buy order (Polymarket hard minimum)
Polymarket rejects market orders below $1 USDC. Added CLOB_MIN_ORDER_USDC=1
constant and applied Math.max(config.minTradeSize, CLOB_MIN_ORDER_USDC) at
both the loop entry check and post-fill remainder check so sub-$1 remainders
are silently skipped instead of causing API errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:43:38 +07:00
direkturcrypto 097148c501 fix: cancel all open orders before sell to release CLOB locked balance
Root cause: auto-sell GTC orders lock all shares in the CLOB's internal
ledger. When executeSell runs, it tried to cancel only position.sellOrderId
but this can fail silently, leaving tokens locked. The CLOB then rejects
the new sell with "not enough balance or allowance" because the balance
is committed to the existing GTC order.

Fix:
- Fetch all open orders for the specific tokenId via client.getOpenOrders()
- Cancel all of them with Promise.allSettled (non-fatal per order)
- Wait 600ms after cancellation so the CLOB updates its locked-balance
  ledger before we place the new sell
- Fallback: if getOpenOrders fails, still attempt to cancel by
  position.sellOrderId (previous behaviour) then wait 600ms
- Use correct cancelOrder({ orderID }) object form throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:38:38 +07:00
direkturcrypto ec2c948a00 fix: redeemer executes via Gnosis Safe proxy wallet (same as MM bot)
redeemer.js was calling redeemPositions() directly from the EOA private
key wallet. This is wrong for two reasons:
1. Conditional tokens are held in config.proxyWallet (Gnosis Safe), not
   in the EOA — so the EOA has nothing to redeem
2. No maxPriorityFeePerGas was set, causing Polygon's "gas tip below
   minimum" error (1.5 Gwei sent, 25 Gwei required)

Fix: replace the direct contract call with execSafeCall() (exported from
ctf.js), which is identical to the MM bot's redemption pattern:
- encodes calldata and executes via Safe.execTransaction()
- signed by the EOA, but msg.sender on-chain = proxy wallet
- enforces 30 Gwei minimum priority fee for Polygon
- retries up to 3x on transient RPC errors

Also remove the now-unused NEG_RISK_CTF_ADDRESS local constant
(CTF_ADDRESS and USDC_ADDRESS imported from ctf.js).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:32:01 +07:00
direkturcrypto e82c67824c fix: resolve 'not enough balance or allowance' on sell orders
Two root causes fixed:

1. Missing ERC-1155 setApprovalForAll for copy-trade flow
   - executeBuy: call ensureExchangeApproval() after a successful fill so
     the CTF Exchange is authorised to move tokens before the next sell
   - executeSell: call ensureExchangeApproval() before every sell attempt;
     the function is idempotent (checks isApprovedForAll first, only sends
     a Safe tx if approval is actually missing)

2. Stored shares can exceed real on-chain balance (fee deductions, float
   drift across partial FAK fills)
   - executeSell: query ctf.balanceOf(proxyWallet, tokenId) before selling
   - If on-chain balance is 0 → position is already gone, remove it and skip
   - If on-chain balance < stored shares → adjust down and log the delta
   - Round sell amount to 4 decimal places to avoid sub-unit precision errors

Also add getOnChainTokenBalance() helper using a minimal CTF ABI slice.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:29:10 +07:00
direkturcrypto 69d8d1401d fix: market expiry guard, FAK orders, 2% slippage, sanitize CLOB logs
Market expiry guard (executor.js):
- getMarketOptions() now returns endDateIso, active, acceptingOrders
  from Gamma API response
- executeBuy() skips immediately if market is closed / not accepting orders
- executeBuy() skips if market closes within MIN_MARKET_TIME_LEFT seconds
  (default 300 s = 5 min), logging exact time remaining
- Add MIN_MARKET_TIME_LEFT to config and .env.example

FAK + 2% slippage (executor.js):
- Replace OrderType.FOK with OrderType.FAK for both BUY and SELL
  market orders — eliminates "FOK fully filled or killed" failures
- Reduce slippage from 5% to 2% (price * 1.02 / price * 0.98)
- Fix zero-fill detection: FAK success with 0 shares logs "no liquidity"
  and retries instead of recording a phantom fill

CLOB log sanitization (logger.js + index.js):
- Add sanitizeClobMessage(): strips axios config object (auth headers)
  from [CLOB Client] dumps, keeps only HTTP status + error string
- Add interceptConsole(): overrides console.error/warn globally
- Call interceptConsole() at startup in index.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:18:57 +07:00
direkturcrypto d3e5567c97 feat: MM adaptive cut-loss with profit floor
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>
2026-02-25 02:34:45 +07:00
direkturcrypto 526076fe6e feat: full project overhaul — market maker, sniper, WebSocket watcher, terminal UI
- 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>
2026-02-23 23:03:06 +07:00
direkturcrypto 7c7fad45f3 feat: initial polymarket copy trade tool
- Watcher: polls Data API for trader activity
- Executor: buy/sell with market orders + retry logic
- Position manager: JSON-based state tracking
- Auto-sell: limit orders at profit target
- Redeemer: check & redeem winning positions on-chain
- Config: env-based settings with validation
- DRY_RUN mode for safe testing
2026-02-22 15:38:13 +07:00